/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.common; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.constraint.impl.ContainmentConstraintImpl; import com.emc.storageos.db.client.impl.ColumnField; import com.emc.storageos.db.client.model.AbstractChangeTrackingMap; import com.emc.storageos.db.client.model.AbstractChangeTrackingSet; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.exceptions.DatabaseException; /** * Class provides method for checking dependencies * used by controller and api svc */ public class DbDependencyPurger { private static final Logger _log = LoggerFactory.getLogger(DbDependencyPurger.class); final private DependencyTracker _dependencyTracker; final private DbClient _dbClient; final static int MAX_PERSISTENCE_LIMIT = 1000; /** * Constructor takes db client and DependencyTracker * * @param dbClient * @param dependencyTracker */ public DbDependencyPurger(DbClient dbClient, DependencyTracker dependencyTracker) { _dbClient = dbClient; _dependencyTracker = dependencyTracker; } /** * Constructor takes db client and DataObjectScanner * * @param dbClient * @param dataObjectScanner */ public DbDependencyPurger(DbClient dbClient, DataObjectScanner dataObjectScanner) { _dbClient = dbClient; _dependencyTracker = dataObjectScanner.getDependencyTracker(); } /** * checks to see if any references exist for this uri * uses dependency list created from relational indices * * @param id id of the DataObject * @param type DataObject class name * @return true if dependents found, false otherwise */ public <T extends DataObject> void purge(URI id, Class<T> type) throws DatabaseException { T dataObj = _dbClient.queryObject(type, id); if (dataObj != null) { if (!dataObj.getInactive()) { dataObj.setInactive(true); _dbClient.persistObject(dataObj); } Set<URI> cleared = new HashSet<URI>(); purge(dataObj, cleared); _log.info("Deactivated db_object: type = {}, id = {}", type.toString(), id); } } private <T extends DataObject> void purge(T dataObj, Set<URI> visited) throws DatabaseException { if (dataObj == null || visited.contains(dataObj.getId())) { return; } visited.add(dataObj.getId()); Class<? extends DataObject> type = dataObj.getClass(); List<DependencyTracker.Dependency> dependencyList = _dependencyTracker.getDependencies(type); try { for (DependencyTracker.Dependency dependence : dependencyList) { // Query relational index to see if any dependents exist Class<? extends DataObject> childType = dependence.getType(); ColumnField childField = dependence.getColumnField(); ContainmentConstraint constraint = new ContainmentConstraintImpl(dataObj.getId(), childType, childField); URIQueryResultList list = new URIQueryResultList(); _dbClient.queryByConstraint(constraint, list); if (!list.iterator().hasNext()) { continue; } HashSet<URI> childSet = new HashSet(); // used to remove efficiently identical URIs from the list. while (list.iterator().hasNext()) { childSet.clear(); while (list.iterator().hasNext()) { childSet.add(list.iterator().next()); if (childSet.size() >= MAX_PERSISTENCE_LIMIT) { break; } } List<URI> childUriList = new ArrayList(childSet); List<? extends DataObject> children = _dbClient.queryObjectField(childType, childField.getName(), childUriList); List<DataObject> decommissionedChildren = new ArrayList(); for (DataObject childObj : children) { switch (childField.getType()) { case TrackingSet: case TrackingMap: // assume @indexByKey is set java.beans.PropertyDescriptor pd = childField.getPropertyDescriptor(); Object fieldValue = pd.getReadMethod().invoke(childObj); boolean deactivateChildObj = false; if (fieldValue != null) { // should be always true. if (AbstractChangeTrackingMap.class.isAssignableFrom(pd.getPropertyType())) { AbstractChangeTrackingMap<?> trackingMap = (AbstractChangeTrackingMap<?>) fieldValue; trackingMap.remove(dataObj.getId().toString()); if (trackingMap.isEmpty() && childField.deactivateIfEmpty()) { deactivateChildObj = true; } } else if (AbstractChangeTrackingSet.class.isAssignableFrom(pd.getPropertyType())) { AbstractChangeTrackingSet trackingSet = (AbstractChangeTrackingSet) fieldValue; trackingSet.remove(dataObj.getId().toString()); if (trackingSet.isEmpty() && childField.deactivateIfEmpty()) { deactivateChildObj = true; } } if (deactivateChildObj) { purge(childObj, visited); childObj.setInactive(true); _log.info("Deactivated db_object: type = {}, id = {}", childType.toString(), childObj.getId()); } } break; default: { purge(childObj, visited); childObj.setInactive(true); _log.info("Deactivated db_object: type = {}, id = {}", childType.toString(), childObj.getId()); } } decommissionedChildren.add(childObj); } _dbClient.persistObject(decommissionedChildren); } } } // should never get here.... catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) { _log.error("Unexpected purge error", e); throw DatabaseException.fatals.purgeFailed(e); } } }