/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.client.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.model.DataObject; import com.netflix.astyanax.model.Column; /** * Utility class for accumulating obsolete columns during deserialization */ public class IndexCleanupList implements IndexColumnList { private static final Logger _log = LoggerFactory.getLogger(IndexCleanupList.class); public static final CompositeColumnName INACTIVE_COLUMN = new CompositeColumnName(DataObject.INACTIVE_FIELD_NAME); private Map<String, List<Column<CompositeColumnName>>> _cleanupList; private Map<String, Map<CompositeColumnName, Column>> _currentMap; // _allColMap is a union of _cleanupList and _currentMap. It might be a redundant structure // but it easier to operate. private Map<String, Map<String, List<Column<CompositeColumnName>>>> _allColMap; private Map<String, DataObject> _cleanedObjects; public IndexCleanupList() { _cleanupList = new HashMap<>(); _currentMap = new HashMap<>(); _allColMap = new HashMap<>(); _cleanedObjects = new HashMap<>(); } @Override public void add(String key, Column<CompositeColumnName> column) { Map<CompositeColumnName, Column> colMap = _currentMap.get(key); Map<String, List<Column<CompositeColumnName>>> keyColumns = _allColMap.get(key); if (colMap == null) { colMap = new HashMap<>(); _currentMap.put(key, colMap); keyColumns = new HashMap<>(); _allColMap.put(key, keyColumns); } Column previousCol = colMap.put(column.getName(), column); if (previousCol != null) { // If .inactive is already true, it's not allowed to be set back to false if (column.getName().getOne().equals(INACTIVE_COLUMN.getOne())) { if (previousCol.getBooleanValue() && !column.getBooleanValue()) { // Switching from true (inactive) to false (active), which is not allowed. // throw new // IllegalStateException(String.format("Row with ID \"%s\" contains a change to \"inactive\" field from true to false, which is not supported", // key)); _log.error(String.format("Row with ID '%s' is changed from inactive to active.", key), new RuntimeException()); } } List<Column<CompositeColumnName>> cleanList = _cleanupList.get(key); if (cleanList == null) { cleanList = new ArrayList<>(); _cleanupList.put(key, cleanList); } cleanList.add(previousCol); if (ColumnField.isDeletionMark(column)) { cleanList.add(column); } } String colName = column.getName().getOne(); List<Column<CompositeColumnName>> columns = keyColumns.get(colName); if (columns == null) { columns = new ArrayList<>(); keyColumns.put(colName, columns); } columns.add(column); } @Override public Map<String, List<Column<CompositeColumnName>>> getColumnsToClean() { return Collections.unmodifiableMap(_cleanupList); } @Override public Map<String, List<Column<CompositeColumnName>>> getAllColumns(String key) { return Collections.unmodifiableMap(_allColMap.get(key)); } @Override public boolean isEmpty() { return _cleanupList.isEmpty(); } // Returns a map contains <RowKey, Columns need to remove their index entries because .inactive is true> // If changedOnly is true, we only returns rows those have indexed fields changed // If changedOnly is false, we returns all touched rows with .inactive = true, even no field is actually changed public Map<String, List<Column<CompositeColumnName>>> getIndexesToClean(boolean changedOnly) { Map<String, List<Column<CompositeColumnName>>> mapIndexes = new HashMap<>(); // For each object we have touched for (Map.Entry<String, Map<CompositeColumnName, Column>> entry : _currentMap.entrySet()) { String rowKey = entry.getKey(); Map<CompositeColumnName, Column> colMap = entry.getValue(); if (changedOnly && !_cleanupList.containsKey(rowKey)) { continue; } // Check if this row's final "inactive" column is "true" Column inactiveColumn = colMap.get(INACTIVE_COLUMN); if (inactiveColumn != null && inactiveColumn.getBooleanValue()) { ArrayList<Column<CompositeColumnName>> cols = new ArrayList<Column<CompositeColumnName>>(); for (Column<CompositeColumnName> col : colMap.values()) { // All indexed fields except "inactive" itself need to be removed if (!col.getName().getOne().equals("inactive") && !ColumnField.isDeletionMark(col)) { cols.add(col); } } if (!cols.isEmpty()) { mapIndexes.put(rowKey, cols); } } } return mapIndexes; } public void addObject(String key, DataObject object) { _cleanedObjects.put(key, object); } public DataObject getObject(String key) { return _cleanedObjects.get(key); } }