/* * Lokomo OneCMDB - An Open Source Software for Configuration * Management of Datacenter Resources * * Copyright (C) 2006 Lokomo Systems AB * * This program 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 2 of the License, or (at * your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. * * Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via * paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33 * Danderyd, Sweden. * */ package org.onecmdb.core.utils.xml; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.collections.TreeBag; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.onecmdb.core.IRFC; import org.onecmdb.core.utils.bean.AttributeBean; import org.onecmdb.core.utils.bean.CiBean; import org.onecmdb.core.utils.bean.ValueBean; public class BeanCompare { BeanRFCGenerator rfcGenerator; private Log log = LogFactory.getLog(this.getClass()); public BeanRFCGenerator getRfcGenerator() { return rfcGenerator; } public void setRfcGenerator(BeanRFCGenerator rfcGenerator) { this.rfcGenerator = rfcGenerator; } public void compare(CiBean base, CiBean remote, CiBean local) { // Handle correct sequence of rfc. if (local != null) { // Check if root. if (local.getDerivedFrom() == null || local.getDerivedFrom().equals("null")) { return; } this.rfcGenerator.getScope().referenceBean(local, "DerivedFrom", local.getDerivedFrom()); this.rfcGenerator.getScope().processBean(local.getDerivedFrom()); } if (base == null) { // Two way compare. // Compare with remote only. if (remote == null && local != null) { // Case 8. // Add Bean.. this.rfcGenerator.addCi(local); } if (remote != null && local != null) { // Case 9,10 // Compare local <--> remote compare(local, remote, false); } } else { // Three way compare if (remote == null && local != null) { // Case 1,2 // Removed from remote, exists in base and local. // Will cause conflict here if it's changed. // Compare local <--> base // What to do.... // Add it again... if (local.getAlias().equals(base.getAlias())) { this.rfcGenerator.addCi(local); } else { compare(local, base, true); } } if (remote != null && local != null) { // Case 3,4 // Three way compare. compare(local, base, true); } if (remote != null && local == null) { // Case 7 // Remove this.rfcGenerator.removeCi(remote); } } } /* * Compare two beans. The left bean is the one that * should be the end result. */ public void compare(CiBean left, CiBean right, boolean doDelete) { // DerivedFrom. if (!isObjectEqual(left.getDerivedFrom(), right.getDerivedFrom())) { // Not supported! } // DisplayNameExpression. if (left.getDisplayNameExpression() != null) { if (!isObjectEqual(left.getDisplayNameExpression(), right.getDisplayNameExpression())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyDisplayNameExpr(left); } } if (left.getDescription() != null) { // Description. if (!isObjectEqual(left.getDescription(), right.getDescription())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyDescrption(left); } } // isBlueprint. if (!isObjectEqual(left.isTemplate(), right.isTemplate())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyTemplate(left); } // Attributes. for (AttributeBean leftAtt : left.getAttributes()) { if (leftAtt.isDerived()) { continue; } AttributeBean rightAtt = right.getAttribute(leftAtt.getAlias()); compare(left, leftAtt, rightAtt); } if (doDelete) { // Handle delete. for (AttributeBean rightAtt : right.getAttributes()) { if (rightAtt.isDerived()) { continue; } AttributeBean leftAtt = left.getAttribute(rightAtt.getAlias()); compare(left, leftAtt, rightAtt); } } // Attribute values. Map<String, List<ValueBean>> leftValue = getValueMap(left); Map<String, List<ValueBean>> rightValue = getValueMap(right); // Compare values. for (String key : leftValue.keySet()) { List<ValueBean> leftList = leftValue.get(key); List<ValueBean> rightList = rightValue.get(key); compare(left, leftList, rightList); } if (doDelete) { // Delete. for (String key : rightValue.keySet()) { List<ValueBean> leftList = leftValue.get(key); if (leftList != null) { continue; } List<ValueBean> rightList = rightValue.get(key); compare(left, leftList, rightList); } } } /** * Construct a map of value with the alias name as the key. * * @param bean * @return */ private Map<String, List<ValueBean>> getValueMap(CiBean bean) { Map<String, List<ValueBean>> map = new HashMap<String, List<ValueBean>>(); for (ValueBean vBean : bean.getAttributeValues()) { List<ValueBean> list = map.get(vBean.getAlias()); if (list == null) { list = new ArrayList<ValueBean>(); map.put(vBean.getAlias(), list); } list.add(vBean); } return(map); } /** * Compare checks if obejcts is null. * * @param o1 * @param o2 * @return */ private boolean isObjectEqual(Object o1, Object o2) { if (o1 == null && o2 == null) { return(true); } if (o1 == null && o2 != null) { return(false); } if (o1 != null && o2 == null) { return(false); } return(o1.equals(o2)); } public void compare(CiBean left, CiBean right) { // Alias. if (!isObjectEqual(left.getAlias(), right.getAlias())) { // Only supported if we have ID's on both. if (isObjectEqual(left.getId(), right.getId())) { rfcGenerator.modifyCIAlias(left); } } // DerivedFrom. if (!isObjectEqual(left.getDerivedFrom(), right.getDerivedFrom())) { // Only supported if we have ID's on both. if (isObjectEqual(left.getId(), right.getId())) { rfcGenerator.modifyDerivedFrom(left); } } // DisplayNameExpression. if (left.getDisplayNameExpression() != null) { if (!isObjectEqual(left.getDisplayNameExpression(), right.getDisplayNameExpression())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyDisplayNameExpr(left); } } if (left.getDescription() != null) { // Description. if (!isObjectEqual(left.getDescription(), right.getDescription())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyDescrption(left); } } // isBlueprint. if (!isObjectEqual(left.isTemplate(), right.isTemplate())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyTemplate(left); } } public void compare(CiBean parent, AttributeBean left, AttributeBean right) { if (right == null) { // Compare... if (left.isDerived()) { return; } rfcGenerator.addAttribute(parent, left); return; } if (left == null) { if (right.isDerived()) { return; } rfcGenerator.removeAttribute(parent, right); return; } if (left.getAlias() != null) { if (!isObjectEqual(left.getAlias(), right.getAlias())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyAttributeAlias(parent, left); } } if (left.getDisplayName() != null) { if (!isObjectEqual(left.getDisplayName(), right.getDisplayName())) { // Update id. left.setId(right.getId()); rfcGenerator.modifyAttributeDisplayNameExpr(parent, left); } } if (left.getType() != null) { if (!isObjectEqual(left.getType(), right.getType())) { // Update id. left.setId(right.getId()); // Modify attribute type. rfcGenerator.modifyAttributeType(parent, left); } } if (left.getRefType() != null) { if (!isObjectEqual(left.getRefType(), right.getRefType())) { // Update id. left.setId(right.getId()); // Modify Reference type. rfcGenerator.modifyAttributeRefType(parent, left); } } if (left.getMaxOccurs() != null) { if (!isObjectEqual(left.getMaxOccurs(), right.getMaxOccurs())) { // Update id. left.setId(right.getId()); // Modify Max Occurs. rfcGenerator.modifyMaxOccurs(parent, left); } } if (left.getMinOccurs() != null) { if (!isObjectEqual(left.getMinOccurs(), right.getMinOccurs())) { // Update id. left.setId(right.getId()); // Modify Min Occurs. rfcGenerator.modifyMinOccurs(parent, left); } } if (left.getDescription() != null) { if (!isObjectEqual(left.getDescription(), right.getDescription())) { // Update id. left.setId(right.getId()); // Modfiy Description. rfcGenerator.modifyAttributeDescription(parent, left); } } } public void compare(CiBean parent, List<ValueBean> left, List<ValueBean> right) { if (left == null) { // Remove all values. for (ValueBean vBean : right) { rfcGenerator.removeValue(parent, vBean); } return; } /* if (right == null) { // Remove all values. int index = 0; List<IRFC> rfcs = new ArrayList<IRFC>(); for (ValueBean vBean : left) { rfcs.add(rfcGenerator.addValueRFC(parent, vBean)); index++; } rfcGenerator.addValues(parent, rfcs); return; } */ List<IRFC> rfcs = new ArrayList<IRFC>(); int appendIndex = 0; for (int index = 0; index < left.size(); index++) { ValueBean leftValue = left.get(index); if (leftValue.getValue() == null) { continue; } // get right value. ValueBean rightValue = getValueBean(leftValue, right); if (rightValue != null) { if (leftValue.getValue().equals(rightValue.getValue())) { continue; } // Update id. leftValue.setId(rightValue.getId()); // Modify Value. rfcs.add(rfcGenerator.getModifyValueRFC(parent, leftValue)); } else { int offset = appendIndex; if (right != null) { if (left.size() > 1) { offset += right.size(); } else { AttributeBean attributeTemplate = rfcGenerator.getAttributeTemplate(parent, leftValue.getAlias()); if (attributeTemplate != null) { // Check if we only can have one value. if (attributeTemplate.fetchMaxOccursAsInt() != 1) { offset += right.size(); } } } } rfcs.add(rfcGenerator.getSetValueRFC(parent, leftValue, offset)); appendIndex++; } continue; } rfcGenerator.addValues(parent, rfcs); /* // Handle delete for (ValueBean rightValue : right) { // get right value. ValueBean leftValue = getValueBean(rightValue, left); if (leftValue == null) { rfcGenerator.removeValue(parent, rightValue); } } */ } private ValueBean getValueBean(ValueBean value, List<ValueBean> list) { if (list == null) { return(null); } for (ValueBean valueInList : list) { Long id = valueInList.getId(); if (id != null) { if (id.equals(value.getId())) { return(valueInList); } } // Compare values. if (value.getValue() != null) { if (value.getValue().equals(valueInList.getValue())) { return(valueInList); } } } return(null); } public void compareID(List<CiBean> local, List<CiBean> base) { HashMap<Long, CiBean> baseMap = new HashMap<Long, CiBean>(); HashMap<Long, CiBean> localMap = new HashMap<Long, CiBean>(); List<CiBean> newLocalCi = new ArrayList<CiBean>(); List<CiBean> noIDBaseCi = new ArrayList<CiBean>(); fillCiIDMap(baseMap, noIDBaseCi, base); if (noIDBaseCi.size() != 0) { throw new IllegalArgumentException("Base CI must all have ID!"); } fillCiIDMap(localMap, newLocalCi, local); // Check for new CI's that have bean removed... for (Long localId : localMap.keySet()) { CiBean baseCi = baseMap.get(localId); CiBean localCi = localMap.get(localId); if (baseCi == null) { log.debug("Local CI ID:" + localId + " " + localCi); for (Long id : baseMap.keySet()) { log.debug("\tBase CI ID:" + id + " " + baseMap.get(id)); } throw new IllegalArgumentException("Local CI can not have an ID if not found in base!"); } if (localCi.getAlias().equals(baseCi.getAlias())) { // Check if CI exists in remote if (rfcGenerator.getScope().getRemoteBean(localCi.getAlias()) == null) { // Add new. newLocalCi.add(localCi); continue; } } } // New // Need to sort them so we create them in correct order. List<CiBean> sorted = sortNew(newLocalCi); for (CiBean newCi : sorted) { rfcGenerator.addCi(newCi); } // Deleted List delete = getRemovedItems(baseMap, localMap); for (Iterator iter = delete.iterator(); iter.hasNext();) { CiBean ciBean = (CiBean)iter.next(); rfcGenerator.removeCi(ciBean); } // Modified for (Long localId : localMap.keySet()) { CiBean baseCi = baseMap.get(localId); CiBean localCi = localMap.get(localId); if (baseCi == null) { log.debug("Local CI ID:" + localId + " " + localCi); for (Long id : baseMap.keySet()) { log.debug("\tBase CI ID:" + id + " " + baseMap.get(id)); } throw new IllegalArgumentException("Local CI can not have an ID if not found in base!"); } // check so we don�t have a alias change. if (localCi.getAlias().equals(baseCi.getAlias())) { // Check if CI exists in remote if (rfcGenerator.getScope().getRemoteBean(localCi.getAlias()) == null) { // Ignore, already handled above... continue; } } // Compare internal fields. compare(localCi, baseCi); // Compare Attribute definitions. compareIDAttribute(localCi, localCi.getAttributes(), baseCi.getAttributes()); // Compare values. compareIDValue(localCi, localCi.getAttributeValues(), baseCi.getAttributeValues()); } } class TreeItem { CiBean bean; List<CiBean> children = new ArrayList(); } private void insert(CiBean b, HashMap<String, CiBean> map, HashSet<String> handled, List<CiBean> sorted) { if (handled.contains(b.getAlias())) { return; } CiBean parent = map.get(b.getDerivedFrom()); if (parent != null) { insert(parent, map, handled, sorted); } sorted.add(b); handled.add(b.getAlias()); } private List<CiBean> sortNew(List<CiBean> newLocalCi) { HashMap<String, CiBean> map = new HashMap<String, CiBean>(); // Buuld tree for (CiBean b : newLocalCi) { map.put(b.getAlias(), b); } List<CiBean> sorted = new ArrayList<CiBean>(); // Need to add them in correct order, (derived from) HashSet<String> handled = new HashSet<String>(); for(CiBean b : map.values()) { insert(b, map,handled, sorted); } return(sorted); } private void compareIDAttribute(CiBean parent, List<AttributeBean> local, List<AttributeBean> base) { HashMap<Long, AttributeBean> baseMap = new HashMap<Long, AttributeBean>(); HashMap<Long, AttributeBean> localMap = new HashMap<Long, AttributeBean>(); List<AttributeBean> newLocalAttribute = new ArrayList<AttributeBean>(); List<AttributeBean> noIDBaseAttribute = new ArrayList<AttributeBean>(); fillAttributeIDMap(baseMap, noIDBaseAttribute, base); if (noIDBaseAttribute.size() != 0) { throw new IllegalArgumentException("Base Attribute(s) must all have ID!"); } fillAttributeIDMap(localMap, newLocalAttribute, local); // New for (AttributeBean newAttr : newLocalAttribute) { rfcGenerator.addAttribute(parent, newAttr); } // Deleted List delete = getRemovedItems(baseMap, localMap); for (Iterator iter = delete.iterator(); iter.hasNext();) { AttributeBean aBean = (AttributeBean)iter.next(); rfcGenerator.removeAttribute(parent, aBean); } // Modified attributes. for (Long localId : localMap.keySet()) { AttributeBean baseAttr = baseMap.get(localId); AttributeBean localAttr = localMap.get(localId); if (baseAttr == null) { throw new IllegalArgumentException("Local Attribute can not have an ID if not found in base!"); } compare(parent, localAttr, baseAttr); } } /** * Compare using id as identifers. * @param local * @param base * @return */ private void compareIDValue(CiBean parent, List<ValueBean> local, List<ValueBean> base) { HashMap<Long, ValueBean> baseMap = new HashMap<Long, ValueBean>(); HashMap<Long, ValueBean> localMap = new HashMap<Long, ValueBean>(); List<ValueBean> newLocalValues = new ArrayList<ValueBean>(); List<ValueBean> noIDBaseValues = new ArrayList<ValueBean>(); fillValueIDMap(baseMap, noIDBaseValues, base); if (noIDBaseValues.size() != 0) { throw new IllegalArgumentException("Base Values must all have ID!"); } fillValueIDMap(localMap, newLocalValues, local); // Add new values. for (ValueBean newValue: newLocalValues) { rfcGenerator.addValue(parent, newValue); } // Deleted values. List delete = getRemovedItems(baseMap, localMap); for (Iterator iter = delete.iterator(); iter.hasNext();) { ValueBean vBean = (ValueBean)iter.next(); rfcGenerator.removeValue(parent, vBean); } // Modified values. for (Long localId : localMap.keySet()) { ValueBean baseValue = baseMap.get(localId); ValueBean localValue = localMap.get(localId); if (baseValue == null) { throw new IllegalArgumentException("Local value can not have an ID if not found in base!"); } if (!isObjectEqual(baseValue.getValue(), localValue.getValue())) { rfcGenerator.modifyValue(parent, localValue); } } } private List getRemovedItems(HashMap<Long, ? extends Object> base, HashMap<Long, ? extends Object> local) { List result = new ArrayList(); // Delete values. for (Long baseId : base.keySet()) { Object value = local.get(baseId); if (value == null) { //Remove this value. result.add(base.get(baseId)); } } return(result); } private void fillValueIDMap(HashMap<Long, ValueBean> vMap, List<ValueBean> unIdentifiedValues, List<ValueBean> values) { for (ValueBean value : values) { Long id = value.getId(); if (id == null) { unIdentifiedValues.add(value); } else { vMap.put(id, value); } } } private void fillCiIDMap(HashMap<Long, CiBean> ciMap, List<CiBean> unIdentifiedCI, List<CiBean> cis) { for (CiBean ci : cis) { Long id = ci.getId(); if (id == null) { unIdentifiedCI.add(ci); } else { ciMap.put(id, ci); } } } private void fillAttributeIDMap(HashMap<Long, AttributeBean> aMap, List<AttributeBean> unIdentifiedAttributes, List<AttributeBean> attributes) { for (AttributeBean attribute : attributes) { Long id = attribute.getId(); if (id == null) { unIdentifiedAttributes.add(attribute); } else { aMap.put(id, attribute); } } } }