Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
*
* * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *
* * For more information: http://www.orientechnologies.com
*
*/
package com.orientechnologies.orient.core.db.tool;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.command.OCommandOutputListener;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.index.OIndexKeyCursor;
import com.orientechnologies.orient.core.index.OIndexManager;
import com.orientechnologies.orient.core.metadata.OMetadataDefault;
import com.orientechnologies.orient.core.metadata.OMetadataInternal;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.record.impl.ODocumentHelper;
import com.orientechnologies.orient.core.record.impl.ODocumentHelper.ODbRelatedCall;
import com.orientechnologies.orient.core.record.impl.ORecordFlat;
import com.orientechnologies.orient.core.storage.OPhysicalPosition;
import com.orientechnologies.orient.core.storage.ORawBuffer;
import com.orientechnologies.orient.core.storage.OStorage;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import static com.orientechnologies.orient.core.record.impl.ODocumentHelper.makeDbCall;
public class ODatabaseCompare extends ODatabaseImpExpAbstract {
private OStorage storage1;
private OStorage storage2;
private ODatabaseDocumentTx databaseDocumentTxOne;
private ODatabaseDocumentTx databaseDocumentTxTwo;
private boolean compareEntriesForAutomaticIndexes = false;
private boolean autoDetectExportImportMap = true;
private OIndex exportImportHashTable = null;
private int differences = 0;
private boolean compareIndexMetadata = false;
public ODatabaseCompare(String iDb1URL, String iDb2URL, final OCommandOutputListener iListener) throws IOException {
super(null, null, iListener);
listener.onMessage("\nComparing two local databases:\n1) " + iDb1URL + "\n2) " + iDb2URL + "\n");
storage1 = Orient.instance().loadStorage(iDb1URL);
storage1.open(null, null, null);
storage2 = Orient.instance().loadStorage(iDb2URL);
storage2.open(null, null, null);
}
public ODatabaseCompare(String iDb1URL, String iDb2URL, final String userName, final String userPassword,
final OCommandOutputListener iListener) throws IOException {
super(null, null, iListener);
listener.onMessage("\nComparing two local databases:\n1) " + iDb1URL + "\n2) " + iDb2URL + "\n");
databaseDocumentTxOne = new ODatabaseDocumentTx(iDb1URL);
databaseDocumentTxOne.open(userName, userPassword);
databaseDocumentTxTwo = new ODatabaseDocumentTx(iDb2URL);
databaseDocumentTxTwo.open(userName, userPassword);
storage1 = databaseDocumentTxOne.getStorage();
storage2 = databaseDocumentTxTwo.getStorage();
// exclude automatically generated clusters
excludeClusters.add("orids");
excludeClusters.add(OMetadataDefault.CLUSTER_INDEX_NAME);
excludeClusters.add(OMetadataDefault.CLUSTER_MANUAL_INDEX_NAME);
}
public void setCompareIndexMetadata(boolean compareIndexMetadata) {
this.compareIndexMetadata = compareIndexMetadata;
}
public boolean isCompareEntriesForAutomaticIndexes() {
return compareEntriesForAutomaticIndexes;
}
public void setCompareEntriesForAutomaticIndexes(boolean compareEntriesForAutomaticIndexes) {
this.compareEntriesForAutomaticIndexes = compareEntriesForAutomaticIndexes;
}
public void setAutoDetectExportImportMap(boolean autoDetectExportImportMap) {
this.autoDetectExportImportMap = autoDetectExportImportMap;
}
public boolean compare() {
if (isDocumentDatabases() && (databaseDocumentTxOne == null || databaseDocumentTxTwo == null)) {
listener.onMessage("\nPassed in URLs are related to document databases but credentials "
+ "were not provided to open them. Please provide user name + password for databases to compare");
return false;
}
if (!isDocumentDatabases() && (databaseDocumentTxOne != null || databaseDocumentTxTwo != null)) {
listener.onMessage("\nPassed in URLs are not related to document databases but credentials "
+ "were provided to open them. Please do not provide user name + password for databases to compare");
return false;
}
try {
ODocumentHelper.RIDMapper ridMapper = null;
if (autoDetectExportImportMap) {
listener
.onMessage("\nAuto discovery of mapping between RIDs of exported and imported records is switched on, try to discover mapping data on disk.");
exportImportHashTable = (OIndex) databaseDocumentTxTwo.getMetadata().getIndexManager()
.getIndex(ODatabaseImport.EXPORT_IMPORT_MAP_NAME);
if (exportImportHashTable != null) {
listener.onMessage("\nMapping data were found and will be loaded.");
ridMapper = new ODocumentHelper.RIDMapper() {
@Override
public ORID map(ORID rid) {
if (rid == null)
return null;
if (!rid.isPersistent())
return null;
final OIdentifiable result = exportImportHashTable.get(rid);
if (result == null)
return null;
return result.getIdentity();
}
};
} else
listener.onMessage("\nMapping data were not found.");
}
compareClusters();
compareRecords(ridMapper);
if (isDocumentDatabases()) {
compareSchama();
compareIndexes(ridMapper);
}
if (differences == 0) {
listener.onMessage("\n\nDatabases match.");
return true;
} else {
listener.onMessage("\n\nDatabases do not match. Found " + differences + " difference(s).");
return false;
}
} catch (Exception e) {
OLogManager.instance()
.error(this, "Error on comparing database '%s' against '%s'", e, storage1.getName(), storage2.getName());
throw new ODatabaseExportException("Error on comparing database '" + storage1.getName() + "' against '" + storage2.getName()
+ "'", e);
} finally {
storage1.close();
storage2.close();
}
}
private void compareSchama() {
OSchema schema1 = ((OMetadataInternal) databaseDocumentTxOne.getMetadata()).getImmutableSchemaSnapshot();
OSchema schema2 = ((OMetadataInternal) databaseDocumentTxTwo.getMetadata()).getImmutableSchemaSnapshot();
boolean ok = true;
for (OClass clazz : schema1.getClasses()) {
OClass clazz2 = schema2.getClass(clazz.getName());
if (clazz2 == null) {
listener.onMessage("\n- ERR: Class definition " + clazz.getName() + " for DB2 is null.");
continue;
}
if (clazz.getSuperClass() != null) {
if (!clazz.getSuperClass().getName().equals(clazz2.getSuperClass().getName())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same superclass in DB2.");
ok = false;
}
}
if (!clazz.getClassIndexes().equals(clazz2.getClassIndexes())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same defined indexes in DB2.");
ok = false;
}
if (!Arrays.equals(clazz.getClusterIds(), clazz2.getClusterIds())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same defined clusters in DB2.");
ok = false;
}
if (!clazz.getCustomKeys().equals(clazz2.getCustomKeys())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same defined custom keys in DB2.");
ok = false;
}
if ((clazz.getJavaClass() == null && clazz2.getJavaClass() != null)
|| (clazz.getJavaClass() != null && !clazz.getJavaClass().equals(clazz2.getJavaClass()))) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same defined Java class in DB2.");
ok = false;
}
if (clazz.getOverSize() != clazz2.getOverSize()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same defined overSize in DB2.");
ok = false;
}
if (clazz.getDefaultClusterId() != clazz2.getDefaultClusterId()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same defined default cluser id in DB2.");
ok = false;
}
if (clazz.getSize() != clazz2.getSize()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same defined size in DB2.");
ok = false;
}
for (OProperty prop : clazz.declaredProperties()) {
OProperty prop2 = clazz2.getProperty(prop.getName());
if (prop2 == null) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as missed property " + prop.getName()
+ "in DB2.");
ok = false;
continue;
}
if (prop.getType() != prop2.getType()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same type for property "
+ prop.getName() + "in DB2. ");
ok = false;
}
if (prop.getLinkedType() != prop2.getLinkedType()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same linkedtype for property "
+ prop.getName() + "in DB2.");
ok = false;
}
if (prop.getMin() != null) {
if (!prop.getMin().equals(prop2.getMin())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same min for property "
+ prop.getName() + "in DB2.");
ok = false;
}
}
if (prop.getMax() != null) {
if (!prop.getMax().equals(prop2.getMax())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same max for property "
+ prop.getName() + "in DB2.");
ok = false;
}
}
if (prop.getMax() != null) {
if (!prop.getMax().equals(prop2.getMax())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same regexp for property "
+ prop.getName() + "in DB2.");
ok = false;
}
}
if (prop.getLinkedClass() != null) {
if (!prop.getLinkedClass().equals(prop2.getLinkedClass())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same linked class for property "
+ prop.getName() + "in DB2.");
ok = false;
}
}
if (prop.getLinkedClass() != null) {
if (!prop.getCustomKeys().equals(prop2.getCustomKeys())) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same custom keys for property "
+ prop.getName() + "in DB2.");
ok = false;
}
}
if (prop.isMandatory() != prop2.isMandatory()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same mandatory flag for property "
+ prop.getName() + "in DB2.");
ok = false;
}
if (prop.isNotNull() != prop2.isNotNull()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName() + " as not same nut null flag for property "
+ prop.getName() + "in DB2.");
ok = false;
}
if (prop.isReadonly() != prop2.isReadonly()) {
listener.onMessage("\n- ERR: Class definition for " + clazz.getName()
+ " as not same readonly flag setting for property " + prop.getName() + "in DB2.");
ok = false;
}
}
if (!ok) {
++differences;
ok = true;
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void compareIndexes(ODocumentHelper.RIDMapper ridMapper) {
listener.onMessage("\nStarting index comparison:");
boolean ok = true;
final OIndexManager indexManagerOne = makeDbCall(databaseDocumentTxOne, new ODbRelatedCall() {
public OIndexManager call() {
return databaseDocumentTxOne.getMetadata().getIndexManager();
}
});
final OIndexManager indexManagerTwo = makeDbCall(databaseDocumentTxTwo, new ODbRelatedCall() {
public OIndexManager call() {
return databaseDocumentTxTwo.getMetadata().getIndexManager();
}
});
final Collection> indexesOne = makeDbCall(databaseDocumentTxOne,
new ODbRelatedCall>>() {
public Collection> call() {
return indexManagerOne.getIndexes();
}
});
int indexesSizeOne = makeDbCall(databaseDocumentTxTwo, new ODbRelatedCall() {
public Integer call() {
return indexesOne.size();
}
});
int indexesSizeTwo = makeDbCall(databaseDocumentTxTwo, new ODbRelatedCall() {
public Integer call() {
return indexManagerTwo.getIndexes().size();
}
});
if (exportImportHashTable != null)
indexesSizeTwo--;
if (indexesSizeOne != indexesSizeTwo) {
ok = false;
listener.onMessage("\n- ERR: Amount of indexes are different.");
listener.onMessage("\n--- DB1: " + indexesSizeOne);
listener.onMessage("\n--- DB2: " + indexesSizeTwo);
listener.onMessage("\n");
++differences;
}
final Iterator> iteratorOne = makeDbCall(databaseDocumentTxOne,
new ODbRelatedCall>>() {
public Iterator> call() {
return indexesOne.iterator();
}
});
while (makeDbCall(databaseDocumentTxOne, new ODbRelatedCall() {
public Boolean call() {
return iteratorOne.hasNext();
}
})) {
final OIndex indexOne = makeDbCall(databaseDocumentTxOne, new ODbRelatedCall>() {
public OIndex call() {
return iteratorOne.next();
}
});
final OIndex indexTwo = makeDbCall(databaseDocumentTxTwo, new ODbRelatedCall>() {
public OIndex call() {
return indexManagerTwo.getIndex(indexOne.getName());
}
});
if (indexTwo == null) {
ok = false;
listener.onMessage("\n- ERR: Index " + indexOne.getName() + " is absent in DB2.");
++differences;
continue;
}
if (!indexOne.getType().equals(indexTwo.getType())) {
ok = false;
listener.onMessage("\n- ERR: Index types for index " + indexOne.getName() + " are different.");
listener.onMessage("\n--- DB1: " + indexOne.getType());
listener.onMessage("\n--- DB2: " + indexTwo.getType());
listener.onMessage("\n");
++differences;
continue;
}
if (!indexOne.getClusters().equals(indexTwo.getClusters())) {
ok = false;
listener.onMessage("\n- ERR: Clusters to index for index " + indexOne.getName() + " are different.");
listener.onMessage("\n--- DB1: " + indexOne.getClusters());
listener.onMessage("\n--- DB2: " + indexTwo.getClusters());
listener.onMessage("\n");
++differences;
continue;
}
if (indexOne.getDefinition() == null && indexTwo.getDefinition() != null) {
ok = false;
listener.onMessage("\n- ERR: Index definition for index " + indexOne.getName() + " for DB2 is not null.");
++differences;
continue;
} else if (indexOne.getDefinition() != null && indexTwo.getDefinition() == null) {
ok = false;
listener.onMessage("\n- ERR: Index definition for index " + indexOne.getName() + " for DB2 is null.");
++differences;
continue;
} else if (indexOne.getDefinition() != null && !indexOne.getDefinition().equals(indexTwo.getDefinition())) {
ok = false;
listener.onMessage("\n- ERR: Index definitions for index " + indexOne.getName() + " are different.");
listener.onMessage("\n--- DB1: " + indexOne.getDefinition());
listener.onMessage("\n--- DB2: " + indexTwo.getDefinition());
listener.onMessage("\n");
++differences;
continue;
}
final long indexOneSize = makeDbCall(databaseDocumentTxOne, new ODbRelatedCall() {
public Long call() {
return indexOne.getSize();
}
});
final long indexTwoSize = makeDbCall(databaseDocumentTxTwo, new ODbRelatedCall() {
public Long call() {
return indexTwo.getSize();
}
});
if (indexOneSize != indexTwoSize) {
ok = false;
listener.onMessage("\n- ERR: Amount of entries for index " + indexOne.getName() + " are different.");
listener.onMessage("\n--- DB1: " + indexOneSize);
listener.onMessage("\n--- DB2: " + indexTwoSize);
listener.onMessage("\n");
++differences;
}
if (compareIndexMetadata) {
final ODocument metadataOne = indexOne.getMetadata();
final ODocument metadataTwo = indexTwo.getMetadata();
if (metadataOne == null && metadataTwo != null) {
ok = false;
listener.onMessage("\n- ERR: Metadata for index " + indexOne.getName() + " for DB1 is null but for DB2 is not.");
listener.onMessage("\n");
++differences;
} else if (metadataOne != null && metadataTwo == null) {
ok = false;
listener.onMessage("\n- ERR: Metadata for index " + indexOne.getName() + " for DB1 is not null but for DB2 is null.");
listener.onMessage("\n");
++differences;
} else if (metadataOne != null && metadataTwo != null
&& !ODocumentHelper.hasSameContentOf(metadataOne, databaseDocumentTxOne, metadataTwo, databaseDocumentTxTwo, ridMapper)) {
ok = false;
listener.onMessage("\n- ERR: Metadata for index " + indexOne.getName() + " for DB1 and for DB2 are different.");
makeDbCall(databaseDocumentTxOne, new ODbRelatedCall