// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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. package org.apache.cloudstack.network.contrail.management; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import net.juniper.contrail.api.ApiObjectBase; import org.apache.log4j.Logger; import org.apache.cloudstack.api.Identity; public class DBSyncGeneric { private static final Logger s_logger = Logger.getLogger(DBSyncGeneric.class); /* for each synchronization VNC class, following methods * needs to be defined. * For e.q : VirtualNetwork class should have createMethodPrefix+"VirtualNetwork" etc */ private final String createMethodPrefix = "create"; private final String deleteMethodPrefix = "delete"; private final String compareMethodPrefix = "compare"; private final String filterMethodPrefix = "filter"; private final String equalMethodPrefix = "equal"; private final String syncMethodPrefix = "sync"; /* default db, vnc comparators are implemented based on uuid values, * if user defined comparators are required, then only add these methods */ private final String dbComparatorMethodPrefix = "dbComparator"; private final String vncComparatorMethodPrefix = "vncComparator"; /* sync methods implementation object, if implemented in seperate class * set the scope object */ private Object _scope; private HashMap<String, Method> _methodMap; private short _syncMode; public static final short SYNC_MODE_UPDATE = 0; public static final short SYNC_MODE_CHECK = 1; public DBSyncGeneric(Object scope) { this._scope = scope; this._syncMode = SYNC_MODE_UPDATE; setMethodMap(); } public DBSyncGeneric() { this._scope = this; this._syncMode = SYNC_MODE_UPDATE; setMethodMap(); } public void setSyncMode(short mode) { this._syncMode = mode; } public short getSyncMode() { return this._syncMode; } public void setScope(Object scope) { this._scope = scope; setMethodMap(); } public void setMethodMap() { _methodMap = new HashMap<String, Method>(); Method methods[] = _scope.getClass().getMethods(); for (int i = 0; i < methods.length; i++) { _methodMap.put(methods[i].getName(), methods[i]); } } public static String getClassName(Class<?> cls) { String clsname = cls.getName(); int loc = clsname.lastIndexOf('.'); if (loc > 0) { clsname = clsname.substring(loc + 1); } return clsname; } /* * This API can be used to sync a particular vnc class */ public Boolean sync(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String syncMethod = syncMethodPrefix + getClassName(cls); Method method = _methodMap.get(syncMethod); if (method == null) throw new NoSuchMethodException(getClassName(_scope.getClass()) + ":" + syncMethod); return (Boolean)method.invoke(_scope, parameters); } private void create(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String createMethod = createMethodPrefix + getClassName(cls); Method method = _methodMap.get(createMethod); if (method == null) throw new NoSuchMethodException(getClassName(_scope.getClass()) + ":" + createMethod); method.invoke(_scope, parameters); } private void delete(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String deleteMethod = deleteMethodPrefix + getClassName(cls); Method method = _methodMap.get(deleteMethod); if (method == null) throw new NoSuchMethodException(getClassName(_scope.getClass()) + ":" + deleteMethod); method.invoke(_scope, parameters); } private Integer compare(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String compareMethod = compareMethodPrefix + getClassName(cls); Method method = _methodMap.get(compareMethod); if (method == null) throw new NoSuchMethodException(getClassName(_scope.getClass()) + ":" + compareMethod); return (Integer)method.invoke(_scope, parameters); } private Boolean filter(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String filterMethod = filterMethodPrefix + getClassName(cls); Method method = _methodMap.get(filterMethod); if (method == null) { s_logger.debug("Method not implemented: " + getClassName(_scope.getClass()) + ":" + filterMethod); return false; } return (Boolean)method.invoke(_scope, parameters); } private Boolean equal(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String equalMethod = equalMethodPrefix + getClassName(cls); Method method = _methodMap.get(equalMethod); if (method == null) { s_logger.debug("Method not implemented: " + getClassName(_scope.getClass()) + ":" + equalMethod); return true; } return (Boolean)method.invoke(_scope, parameters); } @SuppressWarnings("rawtypes") private Comparator dbComparator(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String dbComparatorMethod = dbComparatorMethodPrefix + getClassName(cls); Method method = _methodMap.get(dbComparatorMethod); if (method == null) return dbComparatorDefault(); return (Comparator)method.invoke(_scope, parameters); } @SuppressWarnings("rawtypes") private Comparator vncComparator(Class<?> cls, Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { String vncComparatorMethod = vncComparatorMethodPrefix + getClassName(cls); Method method = _methodMap.get(vncComparatorMethod); if (method == null) return vncComparatorDefault(); return (Comparator)method.invoke(_scope, parameters); } @SuppressWarnings("rawtypes") public Comparator dbComparatorDefault() { Comparator comparator = new Comparator<Identity>() { @Override public int compare(Identity u1, Identity u2) { return u1.getUuid().compareTo(u2.getUuid()); } }; return comparator; } @SuppressWarnings("rawtypes") public Comparator vncComparatorDefault() { Comparator comparator = new Comparator<ApiObjectBase>() { @Override public int compare(ApiObjectBase u1, ApiObjectBase u2) { return u1.getUuid().compareTo(u2.getUuid()); } }; return comparator; } public static class SyncStats { public int create; public int delete; public int equal; public int diff; public int filter; public StringBuffer logMsg; SyncStats() { logMsg = new StringBuffer(); } void log(String str) { logMsg.append(str); logMsg.append('\n'); } public boolean isSynchronized() { return create == 0 && delete == 0 && diff == 0; } @Override public String toString() { StringBuffer str = new StringBuffer(); str.append("create: " + create); str.append(", delete: " + delete); if (filter > 0) { str.append(", filter: " + filter); } str.append(", equal: " + equal); str.append(", diff:" + diff); return str.toString(); } } public void syncCollections(Class<?> cls, Collection<?> lhsList, Collection<?> rhsList, boolean modifyMode, SyncStats stats) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { java.util.Iterator<?> lhsIter = lhsList.iterator(); java.util.Iterator<?> rhsIter = rhsList.iterator(); Object lhsItem = lhsIter.hasNext() ? lhsIter.next() : null; Object rhsItem = rhsIter.hasNext() ? rhsIter.next() : null; while (lhsItem != null && rhsItem != null) { Integer cmp = this.compare(cls, lhsItem, rhsItem, stats.logMsg); if (cmp < 0) { // Create if (modifyMode) { this.create(cls, lhsItem, stats.logMsg); } stats.create++; lhsItem = lhsIter.hasNext() ? lhsIter.next() : null; } else if (cmp > 0) { // Delete if (!this.filter(cls, rhsItem, stats.logMsg)) { if (modifyMode) { this.delete(cls, rhsItem, stats.logMsg); } stats.delete++; } else { stats.filter++; } rhsItem = rhsIter.hasNext() ? rhsIter.next() : null; } else { // Equal if (this.equal(cls, lhsItem, rhsItem, stats.logMsg)) { stats.equal++; } else { stats.diff++; } lhsItem = lhsIter.hasNext() ? lhsIter.next() : null; rhsItem = rhsIter.hasNext() ? rhsIter.next() : null; } } while (lhsItem != null) { // Create if (modifyMode) { this.create(cls, lhsItem, stats.logMsg); } stats.create++; lhsItem = lhsIter.hasNext() ? lhsIter.next() : null; } while (rhsItem != null) { // Delete if (!this.filter(cls, rhsItem, stats.logMsg)) { if (modifyMode) { this.delete(cls, rhsItem, stats.logMsg); } stats.delete++; } else { stats.filter++; } rhsItem = rhsIter.hasNext() ? rhsIter.next() : null; } } @SuppressWarnings("unchecked") public boolean syncGeneric(Class<?> cls, List<?> dbList, List<?> vncList) throws Exception { SyncStats stats = new SyncStats(); stats.log("Sync log for <" + getClassName(cls) + ">"); s_logger.debug("Generic db sync : " + getClassName(cls)); java.util.Collections.sort(dbList, this.dbComparator(cls)); java.util.Collections.sort(vncList, this.vncComparator(cls)); syncCollections(cls, dbList, vncList, _syncMode != SYNC_MODE_CHECK, stats); if (_syncMode != SYNC_MODE_CHECK) { s_logger.debug("Sync stats<" + getClassName(cls) + ">: " + stats.toString()); s_logger.debug(stats.logMsg); s_logger.debug("Generic db sync : " + getClassName(cls) + " done"); } else { s_logger.debug("Sync state checking stats<" + getClassName(cls) + ">: " + stats.toString()); if (!stats.isSynchronized()) { s_logger.debug("DB and VNC objects out of sync is detected : " + getClassName(cls)); s_logger.debug("Log message: \n" + stats.logMsg); } else { s_logger.debug("DB and VNC objects are in sync : " + getClassName(cls)); } } /* return value of this method indicates state of the db & vnc before sync * false: out of sync, true: in sync; * it does not indicate whether sync operation is performed or not; * Actual sync is done only if _syncMode is UPDATE */ return stats.isSynchronized(); } }