| // 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 separate 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(); |
| } |
| |
| } |