/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.dbcli;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.impl.ColumnField;
import com.emc.storageos.db.client.impl.DataObjectType;
import com.emc.storageos.db.client.impl.DbClientImpl;
import com.emc.storageos.db.client.impl.TypeMap;
import com.emc.storageos.db.client.model.Cf;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.Encrypt;
import com.emc.storageos.db.client.model.FSExportMap;
import com.emc.storageos.db.client.model.Name;
import com.emc.storageos.db.client.model.NamedURI;
import com.emc.storageos.db.client.model.OpStatusMap;
import com.emc.storageos.db.client.model.SMBShareMap;
import com.emc.storageos.db.client.model.ScopedLabelSet;
import com.emc.storageos.db.client.model.StringMap;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.StringSetMap;
import com.emc.storageos.db.common.DataObjectScanner;
import com.emc.storageos.db.common.DependencyChecker;
import com.emc.storageos.db.common.DependencyTracker;
import com.emc.storageos.db.common.PackageScanner;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.emc.storageos.dbcli.wrapper.FSExportMapWrapper;
import com.emc.storageos.dbcli.wrapper.OpStatusMapWrapper;
import com.emc.storageos.dbcli.wrapper.SMBShareMapWrapper;
import com.emc.storageos.dbcli.wrapper.ScopedLabelSetWrapper;
import com.emc.storageos.dbcli.wrapper.StringMapWrapper;
import com.emc.storageos.dbcli.wrapper.StringSetMapWrapper;
import com.emc.storageos.dbcli.wrapper.StringSetWrapper;
public class DbCli {
private static final Logger log = LoggerFactory.getLogger(DbCli.class);
DbClientImpl _dbClient = null;
DataObjectScanner dataObjectscanner = null;
private DependencyChecker _dependencyChecker = null;
HashMap<String, Class> _cfMap = new HashMap<String, Class>();
private static final boolean DEBUG = false;
private static final String pkgs = "com.emc.storageos.db.client.model";
private static final String QUITCHAR = "q";
private int listLimit = 100;
private boolean turnOnLimit = false;
private boolean activeOnly = false;
Document doc = null;
Element schemaNode = null;
public enum DbCliOperation {
LIST, DUMP, LOAD, CREATE
}
public DbCli() {
DataObjectModelPackageScanner dataObjectModelPackageScanner = new DataObjectModelPackageScanner();
_cfMap = dataObjectModelPackageScanner.getCfMaps();
}
/**
* Initiate the dbclient
*/
public void initDbClient() {
try {
System.out.println("Initializing db client ...");
_dbClient.start();
} catch (Exception e) {
System.err.println("Caught Exception: " + e);
log.error("Caught Exception: ", e);
}
}
public DbClientImpl getDbClient() {
return _dbClient;
}
public void setDbClient(DbClientImpl dbClient) {
this._dbClient = dbClient;
}
public DataObjectScanner getDataObjectscanner() {
return dataObjectscanner;
}
public void setDataObjectscanner(DataObjectScanner dataObjectscanner) {
this.dataObjectscanner = dataObjectscanner;
}
public void stop() {
if (_dbClient != null) {
_dbClient.stop();
}
}
public void start(boolean skipMigrationCheck) {
_dbClient.setBypassMigrationLock(skipMigrationCheck);
_dbClient.start();
}
/**
* Print column families.
*/
public void printCfMaps() {
Iterator it = _cfMap.entrySet().iterator();
while (it.hasNext()) {
Entry entry = (Entry) it.next();
System.out.println(String.format("\t\tColumn family: %s", entry.getKey()));
}
}
/**
* Print the fields' info of column family.
*
* @Param cfName
*/
public void printFieldsByCf(String cfName) {
final Class clazz = getClassFromCFName(cfName); // fill in type from cfName
if(clazz == null) {
return;
}
if (DataObject.class.isAssignableFrom(clazz)) {
DataObjectType doType = TypeMap.getDoType(clazz);
System.out.println(String.format("Column Family: %s", doType.getCF().getName()));
Collection<ColumnField> cfs = doType.getColumnFields();
Iterator it = cfs.iterator();
while (it.hasNext()) {
ColumnField field = (ColumnField) it.next();
System.out.println(String.format("\tfield=%-30s\ttype=%s", field.getName(),
field.getPropertyDescriptor().getPropertyType().toString().substring(6)));
}
}
}
/**
* Load xml file and persist model object
*
* @Param fileName
*/
public void loadFileAndPersist(String fileName) {
try {
readXMLAndPersist(fileName, DbCliOperation.LOAD);
System.out.println(String.format("Load from file: %s successfully", fileName));
log.info("Load from file: {} successfully", fileName);
} catch (Exception e) {
System.err.println("Caught Exception: " + e);
log.error("Caught Exception: ", e);
}
}
/**
* Load xml file, create and persist model object
*
* @Param fileName
*/
public void loadFileAndCreate(String fileName) {
try {
readXMLAndPersist(fileName, DbCliOperation.CREATE);
System.out.println(String.format("Load and create from file: %s successfully", fileName));
log.info("Load and create from file: {} successfully", fileName);
} catch (Exception e) {
System.err.println("Caught Exception: " + e);
log.error("Caught Exception: ", e);
}
}
/**
* Load xml file and save model object into Cassandra.
*
* @Param fileName
*/
private <T extends DataObject> void readXMLAndPersist(String fileName, DbCliOperation operation) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(fileName);
// Read root node
Element root = doc.getDocumentElement();
Element dataObjectNode = (Element) root.getElementsByTagName("data_object_schema").item(0);
// Get column family's name
String cfName = dataObjectNode.getAttribute("name");
System.out.println("Column Family based on XML: " + cfName);
NodeList recordNodes = dataObjectNode.getElementsByTagName("record");
Class<T> clazz = _cfMap.get(cfName);
if (clazz == null) {
System.out.println("Unknown Column Family: " + cfName);
return;
}
// Get class info
BeanInfo bInfo;
try {
bInfo = Introspector.getBeanInfo(clazz);
} catch (IntrospectionException ex) {
throw new RuntimeException("Unexpected exception getting bean info", ex);
}
PropertyDescriptor[] pds = bInfo.getPropertyDescriptors();
// get position of xml node
InputStream xmlIs = new FileInputStream(new File(fileName));
Document docForPosition = PositionalXMLReader.readXML(xmlIs);
xmlIs.close();
for (int i = 0; i < recordNodes.getLength(); i++) {
Element record = (Element) recordNodes.item(i);
T object = null;
String idStr = null;
if (operation == DbCliOperation.LOAD) {// query record based id
String recordId = record.getAttribute("id");
System.out.println(String.format("Object id:\t%s", recordId));
idStr = recordId;
object = queryObject(URI.create(recordId), clazz);
} else if (operation == DbCliOperation.CREATE) { // create new id for create record
URI id = URIUtil.createId(clazz);
object = clazz.newInstance();
object.setId(id);
System.out.println(String.format("Create new data object id:\t%s", object.getId()));
idStr = object.getId().toString();
}
HashMap<String, String> fieldValueMap = new HashMap<String, String>();
HashMap<String, Class> fieldTypeMap = new HashMap<String, Class>();
HashMap<String, String> fieldLocationMap = new HashMap<String, String>();
HashMap<String, Node> fieldNodeMap = new HashMap<String, Node>();
NodeList fields = record.getElementsByTagName("field");
// get field info from xml file
for (int j = 0; j < fields.getLength(); j++) {
Element field = (Element) fields.item(j);
if (DEBUG) {
System.out.println(field.getAttribute("name") + "\t" + field.getAttribute("type") + "\t" + field.getAttribute("value"));
}
fieldValueMap.put(field.getAttribute("name"), field.getAttribute("value"));
fieldTypeMap.put(field.getAttribute("name"), Class.forName(field.getAttribute("type")));
fieldLocationMap.put(field.getAttribute("name"),
((Element) docForPosition.getElementsByTagName("record").item(i)).getElementsByTagName("field").item(j)
.getUserData("lineNumber").toString());
if (field.getElementsByTagName("wrapper").item(0) != null) {
fieldNodeMap.put(field.getAttribute("name"), field.getElementsByTagName("wrapper").item(0));
}
}
Iterator locationIt = fieldLocationMap.entrySet().iterator();
while (locationIt.hasNext()) {
Entry entry = (Entry) locationIt.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (DEBUG) {
System.out.println("key:\t" + key + "\tvalue\t" + value);
}
}
// update object's fields
for (PropertyDescriptor pd : pds) {
// skip class property, id property
if (pd.getName().equals("class") || pd.getName().equals("id")) {
continue;
}
Name name = pd.getReadMethod().getAnnotation(Name.class);
if (name == null) {
log.info(
"Ignore data object fields without @Name annotation, fieldName={}.",
pd.getName());
continue;
}
String objKey = name.value();
String fieldValue = fieldValueMap.get(objKey);
if (fieldValue == null) {
//To support xml file that the old version dumped, it used method name not @Name value
objKey = pd.getName();
}
fieldValue = fieldValueMap.get(objKey);
Class fieldClass = fieldTypeMap.get(objKey);
String fieldLocation = fieldLocationMap.get(objKey);
Node fieldNode = fieldNodeMap.get(objKey);
if (fieldValue != null) {
Class type = pd.getPropertyType();
if (DEBUG) {
System.out.print("\t" + objKey + " = " + type);
}
try {
if (type == URI.class) {
pd.getWriteMethod().invoke(object, URI.create(fieldValue));
} else if (type == NamedURI.class) {
pd.getWriteMethod().invoke(object, NamedURI.fromString(fieldValue));
} else if (type == Date.class) {
// Can not find records with value which owns this type. Remains to be verified correct or not.
// System.out.println("\ttype: Date ");
} else if (type == Calendar.class) {
Calendar calendar = FieldType.toCalendar(fieldValue);
if (!verifyField(calendar)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, calendar);
} else if (type == StringMap.class) {
StringMap newStringMap = FieldType.convertType(fieldNode, StringMapWrapper.class);
if (!verifyField(newStringMap)) {
throw new Exception("field format exception");
}
StringMap sMap = (StringMap) pd.getReadMethod().invoke(object);
if (sMap == null) {
sMap = new StringMap();
}
sMap.clear();
Set<String> keys = newStringMap.keySet();
for (String key : keys) {
sMap.put(key, newStringMap.get(key));
}
pd.getWriteMethod().invoke(object, sMap);
} else if (type == StringSet.class) {
StringSet stringSet = FieldType.convertType(fieldNode, StringSetWrapper.class);
if (!verifyField(stringSet)) {
throw new Exception("field format exception");
}
StringSet updateSet = (StringSet) pd.getReadMethod().invoke(object);
if (updateSet != null) {
updateSet.clear();
updateSet.addAll(stringSet);
} else {
pd.getWriteMethod().invoke(object, stringSet);
}
} else if (type == OpStatusMap.class) {
OpStatusMap opStatusMap = FieldType.convertType(fieldNode, OpStatusMapWrapper.class);
if (!verifyField(opStatusMap)) {
throw new Exception("field format exception");
}
} else if (type == StringSetMap.class) {
StringSetMap newSetMap = FieldType.convertType(fieldNode, StringSetMapWrapper.class);
if (!verifyField(newSetMap)) {
throw new Exception("field format exception");
}
StringSetMap sMap = (StringSetMap) pd.getReadMethod().invoke(object);
if (sMap == null) {
sMap = new StringSetMap();
}
Set<String> keys = sMap.keySet();
for (String key : keys) {
sMap.remove(key);
}
keys = newSetMap.keySet();
for (String key : keys) {
sMap.put(key, newSetMap.get(key));
}
} else if (type == FSExportMap.class) {
FSExportMap fSExportMap = FieldType.convertType(fieldNode, FSExportMapWrapper.class);
if (!verifyField(fSExportMap)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, fSExportMap);
} else if (type == SMBShareMap.class) {
SMBShareMap sMBShareMap = FieldType.convertType(fieldNode, SMBShareMapWrapper.class);
if (!verifyField(sMBShareMap)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, sMBShareMap);
} else if (type == ScopedLabelSet.class) {
ScopedLabelSet scopedLabelSet = FieldType.convertType(fieldNode, ScopedLabelSetWrapper.class);
if (!verifyField(scopedLabelSet)) {
throw new Exception("field format exception");
}
ScopedLabelSet updateSet = (ScopedLabelSet) pd.getReadMethod().invoke(object);
if (updateSet != null) {
updateSet.clear();
updateSet.addAll(scopedLabelSet);
} else {
pd.getWriteMethod().invoke(object, scopedLabelSet);
}
} else if (type == String.class) {
pd.getWriteMethod().invoke(object, fieldClass.cast(fieldValue));
} else if (type.isEnum()) {
Object enumTypeObject = null;
try {
enumTypeObject = Enum.valueOf(type, fieldValue);
} catch (Exception e) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, enumTypeObject);
} else if (type == Integer.class) {
Integer intNum = FieldType.toInteger(fieldValue);
if (!verifyField(intNum)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, intNum);
} else if (type == Boolean.class) {
Boolean boolVal = FieldType.toBoolean(fieldValue);
if (!verifyField(boolVal)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, boolVal);
} else if (type == Long.class) {
Long longNum = FieldType.toLong(fieldValue);
if (!verifyField(longNum)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, longNum);
} else if (type == Short.class) {
Short shortNum = FieldType.toShort(fieldValue);
if (!verifyField(shortNum)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, shortNum);
} else if (type == Double.class) {
Double doubleNum = FieldType.toDouble(fieldValue);
if (!verifyField(doubleNum)) {
throw new Exception("field format exception");
}
pd.getWriteMethod().invoke(object, doubleNum);
} else {
pd.getWriteMethod().invoke(object, fieldValue);
}
} catch (Exception e) {
System.out
.println(String.format("Exception in getting field:%s in xml file line:%s.", pd.getName(), fieldLocation));
log.error("Exception in getting field value in xml file line:{}.", fieldLocation, e);
throw new Exception(String.format("Exception in getting field value in line:%s.", fieldLocation));
}
if (DEBUG) {
Object fieldValue1 = pd.getReadMethod().invoke(object);
System.out.println("write " + fieldValue1 + "\ttype: " + type + " success");
}
}
}
if (operation == DbCliOperation.CREATE) {
_dbClient.createObject(object);// Save model object.
} else if (operation == DbCliOperation.LOAD) {
_dbClient.persistObject(object);
}
log.info(String.format("Successfully update Column family:%s, \tdata object id:%s \tinto Cassandra, based on xml file %s",
cfName, idStr, fileName));
}
}
private boolean verifyField(Object fieldObject) {
if (fieldObject == null) {
return false;
}
return true;
}
/**
* Query for a record with the given id and type, and print the contents in human readable format
* if query URI list, use queryAndPrintRecords(ids, clazz) method instead.
*
* @param id
* @param clazz
* @param <T>
*/
private <T extends DataObject> void queryAndPrintRecord(URI id, Class<T> clazz, DbCliOperation operationType) throws Exception {
T object = queryObject(id, clazz);
if (object == null) {
// id object deleted
System.out.println("id: " + id + " [ Deleted ]");
return;
}
BeanInfo bInfo;
try {
bInfo = Introspector.getBeanInfo(clazz);
} catch (IntrospectionException ex) {
throw new RuntimeException("Unexpected exception getting bean info", ex);
}
if (operationType == DbCliOperation.LIST) {
printBeanProperties(bInfo.getPropertyDescriptors(), object);
} else {
dumpBeanProperties(bInfo.getPropertyDescriptors(), object);
}
}
/**
* Initiate the root node in xml file
*
* @Param cfName
*/
private void initDumpXmlFile(String cfName) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = dbf.newDocumentBuilder();
} catch (Exception e) {
System.err.println("Caught Exception: " + e);
log.error("Caught Exception: ", e);
}
doc = builder.newDocument();
Element root = doc.createElement("dbschemas");
doc.appendChild(root);
schemaNode = doc.createElement("data_object_schema");
schemaNode.setAttribute("name", cfName);
root.appendChild(schemaNode);
}
/**
* Write model object records into xml file
*
* @Param outFileName
*/
private void writeToXmlFile(String outFileName) {
try {
FileOutputStream fos = new FileOutputStream(outFileName);
OutputStreamWriter outwriter = new OutputStreamWriter(fos);
callWriteXmlFile(doc, outwriter, "utf-8");
outwriter.close();
fos.close();
System.out.println(String.format("Dump into file: %s successfully", outFileName));
log.info("Dump into file: {} successfully", outFileName);
} catch (Exception e) {
System.err.println("Caught Exception: " + e);
log.error("Caught Exception: ", e);
}
}
/**
* Dump the contents in xml format
*
* @param pds
* @param object
* @throws Exception
*/
private <T extends DataObject> void dumpBeanProperties(PropertyDescriptor[] pds,
T object) throws Exception {
Element record = doc.createElement("record");
record.setAttribute("id", object.getId().toString());
schemaNode.appendChild(record);
// Add readOnlyField node.
Element readOnlyElement = doc.createElement("readOnlyField");
record.appendChild(readOnlyElement);
System.out.println("id: " + object.getId().toString());
Object objValue;
Class type;
for (PropertyDescriptor pd : pds) {
objValue = pd.getReadMethod().invoke(object);
if (objValue == null) {
continue;
}
// Skip password property.
if (pd.getName().toLowerCase().matches("[a-zA-Z\\d]*password[a-zA-Z\\d]*")) {
continue;
}
// Skip some properties.
if (pd.getName().equals("class") || pd.getName().equals("id")) {
Element readOnlyfieldNode = doc.createElement("field");
readOnlyfieldNode.setAttribute("type", pd.getPropertyType().toString().substring(6)); // delete the prefix string "class "
readOnlyfieldNode.setAttribute("name", pd.getName().toString());
readOnlyfieldNode.setAttribute("value", objValue.toString());
readOnlyElement.appendChild(readOnlyfieldNode);
continue;
}
// Skip the fields without @Name annotation
Name name = pd.getReadMethod().getAnnotation(Name.class);
if (name == null) {
log.info(
"Ignore data object fields without @Name annotation, fieldName={}.",
pd.getName());
continue;
}
String objKey = name.value(); //use value from @Name instead of mtehod name
type = pd.getPropertyType();
if (DEBUG) {
System.out.print("\t" + pd.getPropertyType() + "\t" + objKey + " = ");
}
Element fieldNode = doc.createElement("field");
fieldNode.setAttribute("type", type.toString().substring(6)); // delete the prefix string "class "
fieldNode.setAttribute("name", objKey);
if (type == StringSetMap.class) {
StringSetMap stringSetMap = (StringSetMap) objValue;
FieldType.marshall(stringSetMap, fieldNode, StringSetMapWrapper.class);
} else if (type == StringSet.class) {
StringSet stringSet = (StringSet) objValue;
FieldType.marshall(stringSet, fieldNode, StringSetWrapper.class);
} else if (type == ScopedLabelSet.class) {
ScopedLabelSet scopedLabelSet = (ScopedLabelSet) objValue;
FieldType.marshall(scopedLabelSet, fieldNode, ScopedLabelSetWrapper.class);
} else if (type == OpStatusMap.class) {
OpStatusMap opStatusMap = (OpStatusMap) objValue;
FieldType.marshall(opStatusMap, fieldNode, OpStatusMapWrapper.class);
} else if (type == StringMap.class) {
StringMap stringMap = (StringMap) objValue;
FieldType.marshall(stringMap, fieldNode, StringMapWrapper.class);
} else if (type == FSExportMap.class) {
FSExportMap fSExportMap = (FSExportMap) objValue;
FieldType.marshall(fSExportMap, fieldNode, FSExportMapWrapper.class);
} else if (type == SMBShareMap.class) {
SMBShareMap sMBShareMap = (SMBShareMap) objValue;
FieldType.marshall(sMBShareMap, fieldNode, SMBShareMapWrapper.class);
} else {
fieldNode.setAttribute("value", objValue.toString());
}
record.appendChild(fieldNode);
}
}
/**
* Print the contents in human readable format
*
* @param pds
* @param object
* @throws Exception
*/
private <T extends DataObject> void printBeanProperties(PropertyDescriptor[] pds,
T object) throws Exception {
System.out.println("id: " + object.getId().toString());
Object objValue;
Class type;
for (PropertyDescriptor pd : pds) {
// skip class property
if (pd.getName().equals("class") || pd.getName().equals("id")) {
continue;
}
Name nameAnnotation = pd.getReadMethod().getAnnotation(Name.class);
String objKey;
if (nameAnnotation == null) {
objKey = pd.getName();
} else {
objKey = nameAnnotation.value();
}
objValue = pd.getReadMethod().invoke(object);
if (objValue == null) {
continue;
}
System.out.print("\t" + objKey + " = ");
Encrypt encryptAnnotation = pd.getReadMethod().getAnnotation(Encrypt.class);
if (encryptAnnotation != null) {
System.out.println("*** ENCRYPTED CONTENT ***");
continue;
}
type = pd.getPropertyType();
if (type == URI.class) {
System.out.println("URI: " + objValue);
} else if (type == StringMap.class) {
System.out.println("StringMap " + objValue);
} else if (type == StringSet.class) {
System.out.println("StringSet " + objValue);
} else if (type == StringSetMap.class) {
System.out.println("StringSetMap " + objValue);
} else if (type == OpStatusMap.class) {
System.out.println("OpStatusMap " + objValue);
} else {
System.out.println(objValue);
}
}
System.out.println();
}
/**
* Query and dump into xml for a particular id in a ColumnFamily
*
* @param cfName
* @param ids
* @throws Exception
*/
@SuppressWarnings("unchecked")
public void queryForDump(String cfName, String fileName, String[] ids) throws Exception {
final Class clazz = getClassFromCFName(cfName); // fill in type from cfName
if(clazz == null) {
return;
}
initDumpXmlFile(cfName);
for (String id : ids) {
queryAndPrintRecord(URI.create(id), clazz, DbCliOperation.DUMP);
}
writeToXmlFile(fileName);
}
/**
* Query and list for a particular id in a ColumnFamily
*
* @param cfName
* @param ids
* @throws Exception
*/
@SuppressWarnings("unchecked")
public void queryForList(String cfName, String[] ids) throws Exception {
final Class clazz = getClassFromCFName(cfName); // fill in type from cfName
if(clazz == null) {
return;
}
for (String id : ids) {
queryAndPrintRecord(URI.create(id), clazz, DbCliOperation.LIST);
}
}
private <T extends DataObject> T queryObject(URI id, Class<T> clazz) throws Exception {
T object = null;
try {
object = _dbClient.queryObject(clazz, id);
} catch (DatabaseException ex) {
System.out.println("Error querying from db: " + ex);
throw ex;
}
return object;
}
/**
* Write contents into xml file
*
* @Param doc
* @Param writer
* @Param encoding
*/
public static void callWriteXmlFile(Document doc, Writer w, String encoding) {
try {
Source source = new DOMSource(doc);
Result result = new StreamResult(w);
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
xformer.setOutputProperty(OutputKeys.ENCODING, encoding);
xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
xformer.transform(source, result);
} catch (TransformerConfigurationException e) {
System.err.println("Caught TransformerConfigurationException" + e);
log.error("Caught TransformerConfigurationException: ", e);
} catch (TransformerException e) {
System.err.println("Caught TransformerException" + e);
log.error("Caught TransformerException: ", e);
}
}
/**
* Delete objects.
*
* @Param cfName
* @Param ids
* @Param force
*/
public void deleteRecords(String cfName, String[] ids, boolean force) {
for (String id : ids) {
try {
delete(id, cfName, force);
} catch (Exception e) {
System.err.println("Caught exception" + e);
log.error("Caught Exception: ", e);
}
}
}
/**
* Delete object.
*
* @param id
* @param cfName
* @param force
*/
private void delete(String id, String cfName, boolean force) throws Exception {
final Class clazz = getClassFromCFName(cfName); // fill in type from cfName
if(clazz == null) {
return;
}
boolean deleted = queryAndDeleteObject(URI.create(id), clazz, force);
if (deleted) {
log.info("The object {} is deleted from the column family {}", id, cfName);
System.out.println(String.format("The object %s is deleted from the column family %s", id, cfName));
}
else {
log.info("The object {} is NOT deleted from the column family {}", id, cfName);
System.out.println(String.format("The object %s is NOT deleted from the column family %s", id, cfName));
}
}
/**
* Query for a record with the given id and type, and print the contents in human readable format
*
* @param id
* @param clazz
* @param <T>
*/
private <T extends DataObject> boolean queryAndDeleteObject(URI id, Class<T> clazz, boolean force)
throws Exception {
if (_dependencyChecker == null) {
DependencyTracker dependencyTracker = dataObjectscanner.getDependencyTracker();
_dependencyChecker = new DependencyChecker(_dbClient, dependencyTracker);
}
if (_dependencyChecker.checkDependencies(id, clazz, false) != null) {
if (!force) {
System.out.println(String.format("Failed to delete the object %s: there are active dependencies", id));
return false;
}
log.info("Force to delete object {} that has active dependencies", id);
}
T object = queryObject(id, clazz);
if (object == null) {
System.out.println(String.format("The object %s has already been deleted", id));
return false;
}
if ((object.canBeDeleted() == null) || force) {
if (object.canBeDeleted() != null) {
log.info("Force to delete object {} that can't be deleted", id);
}
_dbClient.internalRemoveObjects(object);
return true;
}
System.out.println(String.format("The object %s can't be deleted", id));
return false;
}
/**
* Iteratively list records from DB in a user readable format
*
* @param cfName
* @throws Exception
*/
@SuppressWarnings("unchecked")
public void listRecords(String cfName) throws Exception {
final Class clazz = getClassFromCFName(cfName); // fill in type from cfName
if(clazz == null) {
return;
}
List<URI> uris = null;
uris = getColumnUris(clazz, activeOnly);
if (uris == null || !uris.iterator().hasNext()) {
System.out.println("No records found");
return;
}
int count = queryAndPrintRecords(uris, clazz);
System.out.println("Number of All Records is: " + count);
}
/**
* Query for records with the given ids and type, and print the contents in human readable format
*
* @param ids
* @param clazz
* @param <T>
*/
private <T extends DataObject> int queryAndPrintRecords(List<URI> ids, Class<T> clazz)
throws Exception {
Iterator<T> objects;
BeanInfo bInfo;
int countLimit = 0;
int countAll = 0;
String input;
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
try {
objects = _dbClient.queryIterativeObjects(clazz, ids);
bInfo = Introspector.getBeanInfo(clazz);
while (objects.hasNext()) {
T object = (T) objects.next();
printBeanProperties(bInfo.getPropertyDescriptors(), object);
countLimit++;
countAll++;
if (!turnOnLimit || countLimit != listLimit) {
continue;
}
System.out.println(String.format("Read %s rows ", countAll));
do {
System.out.println("\nPress 'ENTER' to continue or 'q<ENTER>' to quit...");
input = buf.readLine();
if (input.isEmpty()) {
countLimit = 0;
break;
}
if (input.equalsIgnoreCase(QUITCHAR)) {
return countAll;
}
} while (!input.isEmpty());
}
} catch (DatabaseException ex) {
log.error("Error querying from db: " + ex);
System.out.println("Error querying from db: " + ex);
throw ex;
} catch (IntrospectionException ex) {
log.error("Unexpected exception getting bean info", ex);
throw new RuntimeException("Unexpected exception getting bean info", ex);
} finally {
buf.close();
}
return countAll;
}
/**
* get the keys of column family for list/count
*/
private List<URI> getColumnUris(Class clazz, boolean isActive) {
List<URI> uris = null;
try {
uris = _dbClient.queryByType(clazz, isActive);
} catch (DatabaseException e) {
System.out.println("Error querying from db: " + e);
return null;
}
return uris;
}
public void setListLimit(int listLimit) {
this.listLimit = listLimit;
}
public void setTurnOnLimit(boolean turnOnLimit) {
this.turnOnLimit = turnOnLimit;
}
public void setActiveOnly(boolean activeOnly) {
this.activeOnly = activeOnly;
}
private static class DataObjectModelPackageScanner extends PackageScanner {
private HashMap<String, Class> cfMap;
public DataObjectModelPackageScanner() {
cfMap = new HashMap<String, Class>();
setPackages("com.emc.storageos.db.client.model");
scan(Cf.class);
}
public HashMap<String, Class> getCfMaps() {
return cfMap;
}
/**
* Processes data object or time series class and extracts CF
* requirements
*
* @param clazz data object or time series class
*/
@Override
protected void processClass(Class clazz) {
if (DataObject.class.isAssignableFrom(clazz)) {
DataObjectType doType = TypeMap.getDoType(clazz);
cfMap.put(doType.getCF().getName(), clazz);
}
}
}
private Class<? extends DataObject> getClassFromCFName(String cfName) {
Class<? extends DataObject> clazz = _cfMap.get(cfName); // fill in type from cfName
if (clazz == null) {
System.err.println("Unknown Column Family: " + cfName);
return null;
}
if (!DataObject.class.isAssignableFrom(clazz)) {
System.err.println("TimeSeries data not supported with this command.");
return null;
}
return clazz;
}
}