/*
* Copyright (c) 2008-2011 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.plugins.common;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.cim.CIMArgument;
import javax.cim.CIMDataType;
import javax.cim.CIMObjectPath;
import javax.cim.CIMProperty;
import javax.wbem.WBEMException;
import javax.wbem.client.WBEMClient;
import com.emc.storageos.cimadapter.connections.cim.CimObjectPathCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.plugins.BaseCollectionException;
import com.emc.storageos.plugins.common.domainmodel.Argument;
import com.emc.storageos.plugins.common.domainmodel.Operation;
/**
* Class having responsiblities like creating Input Arguments from Operation
* Node, returning Instance from Map, Finding out Methods using reflection.
*/
public class Util {
private static final Logger _logger = LoggerFactory.getLogger(Util.class);
public static enum ENDPOINTS {
ARGUMENT,
OPERATION
}
/**
* Return InputArgument Array If argument object has Filter, then compare
* the metrics values got from Framework against the present in Domain
* Logic. Remove metrics from framework list if it is not matching Domain
* Logic. Else if filter is not there, proceed with no change.
*
* @param operation
* : Domain Logic operation.
* @param index
* : index of the Director or Port List.
* @return Object[].
* @throws SMIPluginException
* ex.
* @throws InvocationTargetException
* ex.
* @throws IllegalArgumentException
* ex.
* @throws IllegalAccessException
* ex.
*/
public final Object[] returnInputArgs(
final Operation operation, final Map<String, Object> keyMap, int index)
throws BaseCollectionException, IllegalAccessException, InvocationTargetException {
final List<Object> args = operation.getArguments();
final int nArgs = args.size();
final Object[] inputArgs1 = new Object[nArgs];
int count = 0;
for (Object argobj : args) {
/**
* 1. Get the instance on which the method needs to get executed 2.
* Get the Method 3. Construct Input Argument Array 4. Execute
*/
Argument arg = (Argument) argobj;
// final Object instance = returnInstance(arg.gegetInstance(),
// keyMap);
final Object instance = arg.getCreator();
final Method method = getMethod(operation, arg.getMethod(), instance, Util.ENDPOINTS.ARGUMENT.toString());
final Object[] inputArgs = { arg, keyMap, index };
final Object resultObj = method.invoke(instance, inputArgs);
inputArgs1[count++] = resultObj;
}
return normalizedWriteArgs(keyMap, inputArgs1);
}
/**
* Get the Instance on which method needs to be run.
*
* @param operation
* @param keyMap
* @throws BaseCollectionException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public final Object returnInstanceToRun(
final Operation operation, final Map<String, Object> keyMap, int index)
throws BaseCollectionException, IllegalAccessException,
InvocationTargetException {
Object instanceToReturn = null;
if (operation.getInstance() instanceof Argument) {
Argument instanceArg = (Argument) operation.getInstance();
final Object[] inputArgs = { instanceArg, keyMap, index };
final Object instance = instanceArg.getCreator();
final Method method = getMethod(operation, instanceArg.getMethod(), instance, Util.ENDPOINTS.ARGUMENT.toString());
instanceToReturn = method.invoke(instanceArg.getCreator(), inputArgs);
} else {
instanceToReturn = operation.getInstance();
}
return instanceToReturn;
}
/**
* get Method to execute If operation.Argument Objects size ==
* Method.Parameters size, then select the Method.
*
* @param methodName
* : Method.
* @param operation
* : Domain logic operation.
* @param instance
* : CIMCLient or Request or any 3rd party..
* @return Method.
*/
public final Method getMethod(
final Operation operation, final String methodName, final Object instance,
final String endPoint) {
Method method = null;
try {
Class[] parameterTypeClasses = getParameterTypesFromConfiguration(operation,
endPoint);
if (null == parameterTypeClasses || parameterTypeClasses.length == 0) {
Method[] methods = instance.getClass().getMethods();
for (Method m : methods) {
if (instance instanceof WBEMClient) {
if (m.getName().equalsIgnoreCase(methodName)
&& operation.getArguments().size() == m
.getParameterTypes().length) {
method = m;
_logger.debug("Method found :" + m.getName());
break;
}
} else {
if (m.getName().equalsIgnoreCase(methodName)) {
method = m;
break;
}
}
}
} else {
// find Overloaded Methods by passing expected Argument Classes
method = instance.getClass().getMethod(methodName, parameterTypeClasses);
_logger.debug("Method found :" + method.getName());
}
} catch (SecurityException e) {
_logger.error("Method Not found due to Security Exception : {} -->{}",
methodName, e);
} catch (NoSuchMethodException e) {
_logger.error("Method Not found due to NoSuchMethodException :{} -->{}",
methodName, e);
} catch (ClassNotFoundException e) {
_logger.error("Method Not found due to ClassNotFoundException :{} -->{}",
methodName, e);
}
return method;
}
/**
* get Method based on Parameter Types
*
* @param operation
* @param endPoint
* @return
* @throws ClassNotFoundException
*/
@SuppressWarnings("rawtypes")
private Class[] getParameterTypesFromConfiguration(
Operation operation, String endPoint) throws ClassNotFoundException {
List<Class> classes = new ArrayList<Class>();
Class[] classArray = null;
if (null == operation) {
return classArray;
}
for (int index = 0; index < operation.getArguments().size(); index++) {
String type = getType(operation, endPoint, index);
if (null == type) {
continue;
}
Class expectedClazz = Class.forName(type);
classes.add(expectedClazz);
}
classArray = new Class[classes.size()];
classArray = classes.toArray(classArray);
return classArray;
}
private String getType(Operation operation, String endPoint, int index) {
if (ENDPOINTS.ARGUMENT.toString().equalsIgnoreCase(endPoint)) {
return ((Argument) operation.getArguments().get(index)).getType();
}
return operation.getType();
}
/**
* return the Instance on which the method needs to get executed.
*
* @param instanceKey
* : key.
* @param keyMap
* : datastructure to hold data.
* @return Object.
*/
public Object returnInstance(
final String instanceKey, final Map<String, Object> keyMap) {
Object instance = null;
if (keyMap.containsKey(instanceKey)) {
instance = keyMap.get(instanceKey);
} else {
_logger.error("Instance : {} not found", instanceKey);
/**
* Use reflection to create the instance To-Do
*/
}
return instance;
}
/**
* Does operation have filter. To-Do :future use
*
* @param filterValue
* @param filterValuesFromOp
* @return boolean.
*/
public final boolean doesOpFilterHasDiscoveryFilter(
final Object filterValue, final String[] filterValuesFromOp) {
boolean bOpHasFilterValue = true;
if (filterValue instanceof List<?>) {
@SuppressWarnings("unchecked")
final List<String> filterValuesFromKeymap = (List<String>) filterValue;
boolean bMatched = false;
for (String v : filterValuesFromKeymap) {
for (String s : filterValuesFromOp) {
if (v.equalsIgnoreCase(s)) {
bMatched = true;
break;
}
}
}
if (!bMatched) {
bOpHasFilterValue = false;
}
}
return bOpHasFilterValue;
}
/**
* Print Map values.
*
* @param keyMap
* : common datastructure to hold.
* @return String.
*/
@SuppressWarnings("unchecked")
public final String printMapValues(final Map<String, Object> keyMap) {
final StringBuilder strbuilder = new StringBuilder();
for (Entry<String, Object> keyType : keyMap.entrySet()) {
String key = keyType.getKey();
strbuilder.append("*****************\n");
strbuilder.append("key : ");
strbuilder.append(key);
strbuilder.append("\n");
Object result = keyType.getValue();
if (result instanceof List) {
List<Object> objList = (List<Object>) result;
strbuilder.append("Value List : \n");
for (Object obj : objList) {
strbuilder.append(obj.toString());
strbuilder.append("\n");
}
} else {
strbuilder.append("Value : ");
strbuilder.append(result);
strbuilder.append("\n");
}
}
return strbuilder.toString();
}
/**
* get Message.
*
* @param ex
* WBEMException.
* @return String.
*/
public String getMessage(final WBEMException ex) {
String cause = ex.getCause() != null ? ex.getCause().toString() : "";
String message = ex.getMessage() != null ? ex.getMessage() : "";
String error = "";
if (!cause.isEmpty()) {
error = cause;
}
if (!message.isEmpty()) {
error = error + "." + message;
}
return error;
}
/**
* Method used as a ByPasser
* i.e. if runtime control wants to directly execute a Processor code
* then use this by Passer
*
* @return
*/
public boolean byPassControlToProcessor(boolean flag) {
return true;
}
/**
* This routine will take the arguments and transform any CIMObjectPaths that have
* SMI8.0 delimiters ("-+-") to "+". This will make it so that everything at the ViPR
* controller level knows things in terms of this delimiter. Anything that has to be
* communicated back to the provider will translated back to SMI8.0 delimiters.
*
* @param keyMap [in/out] Map of String to Objects. Context used for scanning and discovery
* @param objects [in] CIM Arguments
* @return objects, possibly modified.
*/
public static Object normalizedReadArgs(Map<String, Object> keyMap, Object... objects) {
int index = 0;
for (Object object : objects) {
if (object instanceof CIMObjectPath && object.toString().contains(Constants.SMIS80_DELIMITER)) {
objects[index] = modifyForViPRConsumption(keyMap, (CIMObjectPath) object);
}
index++;
}
return objects;
}
/**
* This routine will take the arguments and transform any CIMObjectPaths to SMI8.0 delimiters
* if the the USING_SMIS80_DELIMITERS context flag is enabled.
*
* @param keyMap [in/out] Map of String to Objects. Context used for scanning and discovery
* @param objects [in] CIM Arguments
* @return objects, possibly modified
*/
public static Object[] normalizedWriteArgs(Map<String, Object> keyMap, Object[] objects) {
int index = 0;
Boolean using80Delimiters = (Boolean) keyMap.get(Constants.USING_SMIS80_DELIMITERS);
if (using80Delimiters != null && using80Delimiters) {
for (Object object : objects) {
if (object instanceof CIMObjectPath) {
String string = object.toString();
if (string.contains(Constants.SMIS80_DELIMITER)) {
continue;
}
if (!string.contains("SE_ReplicationGroup") && string.contains(Constants.PLUS)) {
objects[index] = modifyForSMIS80(keyMap, (CIMObjectPath) object);
}
} else if (object instanceof CIMArgument) {
// Check any of the argument is CIMObjectPath, if so, it
// would need to be fixed for SMI-8.0 providers
Object modifiedArgument = modifyAnyCIMObjectPaths(keyMap, object);
if (modifiedArgument != null) {
objects[index] = modifiedArgument;
}
} else if (object instanceof CIMArgument[]) {
// Check any of the arguments that are passed for CIMObjects.
// If there are any, fix them for SMI-S 8.0.
boolean updated = false;
CIMArgument[] arguments = (CIMArgument[]) object;
for (int argumentIndex = 0; argumentIndex < arguments.length; argumentIndex++) {
CIMArgument currentArgument = arguments[argumentIndex];
CIMArgument modifiedArgument = modifyAnyCIMObjectPaths(keyMap, currentArgument);
if (modifiedArgument != null) {
arguments[argumentIndex] = modifiedArgument;
updated = true;
}
}
if (updated) {
objects[index] = arguments;
}
}
index++;
}
}
return objects;
}
/**
* This method will take the 'object', which points to a CIMArgument, and determine
* if it holds a CIMObjectPath. If it does it, will do a conversion of the object
* per the expected delimiter for SMI-S 8.0
*
* @param keyMap [in] - Contextual object
* @param object [in] - CIMArgument as an Object
* @return CIMArgument
*/
private static CIMArgument modifyAnyCIMObjectPaths(Map<String, Object> keyMap, Object object) {
if (keyMap == null || object == null) {
return null;
}
CIMArgument modifiedArgument = null;
CIMArgument argument = (CIMArgument) object;
// Only process CIMObjectPath CIMArguments
Object cimPathReferenceObject = argument.getValue();
if (cimPathReferenceObject == null || !(cimPathReferenceObject instanceof CIMObjectPath)) {
return null;
}
// If the path already has the SMI-S 8.0 delimiter, then don't bother
String string = cimPathReferenceObject.toString();
if (string.contains(Constants.SMIS80_DELIMITER)) {
return null;
}
// This has a delimiter in there, so we'll have to modify it
if (!string.contains("SE_ReplicationGroup") && string.contains(Constants.PLUS)) {
Object modifiedPath = modifyForSMIS80(keyMap, (CIMObjectPath) cimPathReferenceObject);
modifiedArgument = new CIMArgument<>(argument.getName(), argument.getDataType(),
(CIMObjectPath) modifiedPath);
}
return modifiedArgument;
}
private static Object modifyForSMIS80(Map<String, Object> keyMap, CIMObjectPath cimObjectPath) {
return modifyCimObjectPathProperties(keyMap, cimObjectPath, Constants.PLUS, Constants.SMIS_PLUS_REGEX, Constants.SMIS80_DELIMITER);
}
private static Object modifyForViPRConsumption(Map<String, Object> keyMap, CIMObjectPath cimObjectPath) {
return modifyCimObjectPathProperties(keyMap, cimObjectPath, Constants.SMIS80_DELIMITER, Constants.SMIS80_DELIMITER_REGEX,
Constants.PLUS);
}
/**
* Generic routine that will take a cimObjectPath and examine it. It will look through the keys and check their
* values for the specified 'delimiter'. If it exists, then all instances of 'searchRegex' will be changed to
* 'replaceWith'.
*
* If there was a modification, keyMap will have a Constants.USING_SMIS80_DELIMITERS set to true.
*
* @param keyMap [in/out] Map of String to Objects. Context used for scanning and discovery. Can pass null if
* this is not necessary to use.
* @param cimObjectPath [in] - CIMObjectPath
* @param delimiter [in] - Check if any key values contain this delimiter
* @param searchRegex [in] - Substring search string
* @param replaceWith [in] - Replacement string
* @return CIMObjectPath, that may have been modified.
*/
private static Object modifyCimObjectPathProperties(Map<String, Object> keyMap, CIMObjectPath cimObjectPath,
String delimiter, String searchRegex, String replaceWith) {
CIMObjectPath result = cimObjectPath;
boolean isModified = false;
CIMProperty<?>[] properties = cimObjectPath.getKeys();
CIMProperty<?>[] changedProperties = new CIMProperty<?>[properties.length];
for (int index = 0; index < properties.length; index++) {
CIMProperty property = properties[index];
Object value = cimObjectPath.getKeyValue(property.getName());
if (value instanceof String) {
String string = (String) value;
changedProperties[index] = property;
if (string.contains(delimiter)) {
String modified = string.replaceAll(searchRegex, replaceWith);
CIMProperty changed =
new CIMProperty<>(property.getName(), CIMDataType.STRING_T, modified, true, false, null);
changedProperties[index] = changed;
isModified = true;
if (keyMap != null) {
keyMap.put(Constants.USING_SMIS80_DELIMITERS, Boolean.TRUE);
}
}
}
}
if (isModified) {
result = CimObjectPathCreator.createInstance(cimObjectPath.getObjectName(),
cimObjectPath.getNamespace(), changedProperties);
}
return result;
}
}