/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.http.conn.util.InetAddressUtils;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.DiscoveredDataObject;
import com.emc.storageos.db.client.util.SizeUtil;
import com.emc.storageos.recoverpoint.utils.WwnUtils;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.ServiceCode;
import com.emc.storageos.svcs.errorhandling.resources.ServiceCodeException;
/**
* Utility functions for validating arguments
*/
public class ArgValidator {
private static final String ALPHA_NUMERIC_PATTERN = "^[a-zA-Z0-9]+$";
private static final Pattern patternAlphanumeric = Pattern.compile(ALPHA_NUMERIC_PATTERN);
private static final String ALPHA_NUMERIC_UNDERSCORE = "^[a-zA-Z0-9_-]*$";
private static final String NUMERIC_PATTERN = "^[0-9]*$";
/**
* Checks input URI and throws APIException.badRequests.invalidURI if
* validation fails
*
* @param uri
* the URN to check
*/
public static void checkUri(final URI uri) {
if (!URIUtil.isValid(uri)) {
throw APIException.badRequests.invalidURI(uri);
}
}
/**
* Validates that the uri supplied is not null, is of the urn:storageos:
* scheme and represents an object of specific type.
*
* @param uri
* the URN to check
* @param type
* the DataObject class that the URI must represent
* @param fieldName
* the name of the field where the uri originated
*/
public static void checkFieldUriType(final URI uri, final Class<? extends DataObject> type, final String fieldName) {
checkFieldNotNull(uri, fieldName);
if (!URIUtil.isValid(uri)) {
throw APIException.badRequests.invalidParameterURIInvalid(fieldName, uri);
}
if (!URIUtil.isType(uri, type)) {
throw APIException.badRequests.invalidParameterURIWrongType(fieldName, uri, type.getSimpleName());
}
}
public static void checkUrl(final String url, final String fieldName) {
try {
new URL(url);// NOSONAR("We are creating an URL object to validate the url string")
} catch (MalformedURLException e) {
throw APIException.badRequests.invalidUrl(fieldName, url);
}
}
/**
* Validates that the value supplied is not null.
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldNotNull(final Object value, final String fieldName) {
checkField(value != null, fieldName);
}
/**
* Validates that the value supplied is not an empty string.
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldNotEmpty(final String value, final String fieldName) {
checkField(StringUtils.isNotEmpty(value), fieldName);
}
/**
* Validates that the value supplied is not null, and not an empty collection
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldNotEmpty(final Collection<?> value, final String fieldName) {
checkField(value != null && !value.isEmpty(), fieldName);
}
/**
* Validates that the value supplied is not null, and not an empty map
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldNotEmpty(final Map<?, ?> value, final String fieldName) {
checkField(value != null && !value.isEmpty(), fieldName);
}
/**
* Fires APIException.badRequests.requiredParameterMissingOrEmpty if given
* condition is false for a field
*
* @param condition
* The condition to check. If false, it will throw a
* ServiceCodeException
* @param fieldName
* The field name to validate
*/
private static void checkField(final boolean condition, final String fieldName) {
if (!condition) {
throw APIException.badRequests.requiredParameterMissingOrEmpty(fieldName);
}
}
/**
* Validates that the value supplied is not null, and matches one of the
* expected values
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
* @param expected
* the set of Enum values to allow
*/
public static <E extends Enum<E>> void checkFieldForValueFromEnum(final E value, final String fieldName,
final EnumSet<E> expected) {
checkFieldNotNull(value, fieldName);
checkFieldValueFromEnum(value.name(), fieldName, expected);
}
/**
* Validates that the value supplied matches one of the expected system values' names
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
* @param expected
* the set of values to allow
*/
public static void checkFieldValueFromSystemType(final String value, final String fieldName,
final Collection<DiscoveredDataObject.Type> expected) {
for (DiscoveredDataObject.Type e : expected) {
if (e.name().equals(value)) {
return;
}
}
throw APIException.badRequests.invalidParameterValueWithExpected(fieldName, value,
expected.toArray());
}
/**
* Validates that the value supplied matches one of the expected values' names
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
* @param expected
* the set of Enum values to allow
*/
public static void checkFieldValueFromEnum(final String value, final String fieldName,
final EnumSet<?> expected) {
for (Enum<?> e : expected) {
if (e.name().equals(value)) {
return;
}
}
throw APIException.badRequests.invalidParameterValueWithExpected(fieldName, value,
expected.toArray());
}
/**
* Validates that the value supplied matches one of the expected values' names
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
* @param enumType
* the enum class the value is expected to be in
*/
public static void checkFieldValueFromEnum(final String value, final String fieldName,
Class<? extends Enum> enumType) {
if (value != null) {
checkFieldValueFromEnum(value, fieldName, EnumSet.allOf(enumType));
}
}
public static void checkReference(final Class<? extends DataObject> type, final URI id, final String depedency) {
if (depedency != null) {
if (depedency.length() == 0) {
throw APIException.badRequests.resourceCannotBeDeleteDueToUnreachableVdc();
} else {
throw APIException.badRequests.resourceHasActiveReferencesWithType(type.getSimpleName(), id, depedency);
}
}
}
public static void checkReference(final Class<? extends DataObject> type, final String label, final String depedency) {
if (depedency != null) {
if (depedency.length() == 0) {
throw APIException.badRequests.resourceCannotBeDeleteDueToUnreachableVdc();
} else {
throw APIException.badRequests.resourceHasActiveReferencesWithType(type.getSimpleName(), label, depedency);
}
}
}
/**
* Validates that the value supplied matches one of the expected values
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
* @param expected
* the set of values to allow ignoring case
*/
public static void checkFieldValueFromArray(final Object value, final String fieldName,
final Object... expected) {
for (Object entry : expected) {
if (entry.equals(value)) {
return;
}
}
throw APIException.badRequests.invalidParameterValueWithExpected(fieldName, value,
expected);
}
/**
* Validates that the value supplied matches one of the expected values ignoring case
*
* @param value
* the value to check
* @param fieldName
* the name of the field where the value originated
* @param expected
* the set of values to allow ignoring case
*/
public static void checkFieldValueFromArrayIgnoreCase(final String value, final String fieldName,
final String... expected) {
for (String entry : expected) {
if (entry.equalsIgnoreCase(value)) {
return;
}
}
throw APIException.badRequests.invalidParameterValueWithExpected(fieldName, value,
expected);
}
/**
* Validates that a condition has passed, providing original and expected
* values for a clear error message
*
* @param condition
* the result of checking the value, if false then an exception will be thrown
* @param fieldName
* the name of the field where the value originated
* @param value
* the value that was checked, used for the error message presentation
* @param expected
* the set of allowable values, used for the error message presentation
*/
public static void checkFieldValueWithExpected(final boolean condition, final String fieldName,
final Object value, final Object... expected) {
if (!condition) {
throw APIException.badRequests.invalidParameterValueWithExpected(fieldName, value,
expected);
}
}
/**
* Validates that a condition has passed, providing original and expected
* values for a clear error message
*
* @param condition
* the result of checking the value, if false then an exception will be thrown
* @param fieldName
* the name of the field where the value originated
* @param value
* the value that was checked, used for the error message presentation
* @param expected
* the set of allowable values, used for the error message presentation
*/
public static void checkFieldValueWithExpected(final boolean condition, final String fieldName,
final Object value, final Collection<Object> expected) {
if (!condition) {
throw APIException.badRequests.invalidParameterValueWithExpected(fieldName, value,
expected);
}
}
/**
* Validates that a named field contains a valid IPv4 or IPv6 address
*
* @param ip
* a string representation of an IPv4 or IPv6 address
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldValidIP(final String ip, final String fieldName) {
checkFieldNotEmpty(ip, fieldName);
if (!isValidIPV4(ip) && !isValidIPV6(ip)) {
throw APIException.badRequests.invalidParameterInvalidIP(fieldName, ip);
}
}
/**
* Validates that a named field contains a valid InetAddress
*
* @param ip
* @param fieldName
*/
public static void checkFieldValidInetAddress(final String ip, final String fieldName) {
checkFieldNotEmpty(ip, fieldName);
if (!validateInetAddress(ip)) {
throw APIException.badRequests.invalidParameterInvalidIP(fieldName, ip);
}
}
private static boolean validateInetAddress(final String address) {
try {
InetAddress.getByName(address);
} catch (UnknownHostException e) {
return false;
}
return true;
}
/**
* Validates that a named field contains a valid IPv4 address
*
* @param ip
* a string representation of an IPv4 address
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldValidIPV4(final String ip, final String fieldName) {
checkFieldNotEmpty(ip, fieldName);
if (!isValidIPV4(ip)) {
throw APIException.badRequests.invalidParameterInvalidIPV4(fieldName, ip);
}
}
/**
* Validates that a named field contains a valid IPv6 address
*
* @param ip
* a string representation of an IPv6 address
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldValidIPV6(final String ip, final String fieldName) {
checkFieldNotEmpty(ip, fieldName);
if (!isValidIPV6(ip)) {
throw APIException.badRequests.invalidParameterInvalidIPV6(fieldName, ip);
}
}
/**
* Validates that the supplied DataObject is not null. Null entities will
* result in a 404 Not Found exception being thrown if idEmbeddedInURL is
* true, or a 400 Bad Request otherwise.
*
* @param object
* the DataObject instance to verify
* @param id
* the id of the null object, used for the error message presentation
* @param idEmbeddedInURL
* true if and only if the id was supplied in the URL
*/
public static void checkEntityNotNull(final DataObject object, final URI id,
boolean idEmbeddedInURL) {
if (object == null) {
if (idEmbeddedInURL) {
throw APIException.notFound.unableToFindEntityInURL(id);
} else {
throw APIException.badRequests.unableToFindEntity(id);
}
}
}
/**
* Validates that the supplied DataObject is not null AND is not inactive.
* Null or inactive entities will result in a 404 Not Found exception
* being thrown if idEmbeddedInURL is true, or a 400 Bad Request otherwise.
*
* @param object
* the DataObject instance to verify
* @param id
* the id of the null object, used for the error message presentation
* @param idEmbeddedInURL
* true if and only if the id was supplied in the URL
*/
public static void checkEntity(final DataObject object, final URI id,
final boolean idEmbeddedInURL) {
checkEntityNotNull(object, id, idEmbeddedInURL);
if (object.getInactive()) {
if (idEmbeddedInURL) {
throw APIException.notFound.entityInURLIsInactive(id);
} else {
throw APIException.badRequests.entityInRequestIsInactive(id);
}
}
}
/**
* Validates that the supplied DataObject is not null AND (conditionally)
* is not inactive. Failing entities will result in a 404 Not Found
* exception being thrown if idEmbeddedInURL is true, or a 400 Bad Request
* otherwise.
*
* @param object
* the DataObject instance to verify
* @param id
* the id of the null object, used for the error message presentation
* @param idEmbeddedInURL
* true if and only if the id was supplied in the URL
* @param checkInactive
* true if and only if an active DataObject is required
*/
public static void checkEntity(final DataObject object, final URI id,
final boolean idEmbeddedInURL, final boolean checkInactive) {
checkEntityNotNull(object, id, idEmbeddedInURL);
if (checkInactive && object.getInactive()) {
if (idEmbeddedInURL) {
throw APIException.notFound.entityInURLIsInactive(id);
} else {
throw APIException.badRequests.entityInRequestIsInactive(id);
}
}
}
/**
* Validates IPV4 address using regex for the given ipAddress
*
* @param ipAddress
* IP Address
* @return {@link Boolean} status flag
*/
private static Boolean isValidIPV4(final String ipAddress) {
boolean status = false;
if (StringUtils.isNotEmpty(ipAddress)) {
status = InetAddressUtils.isIPv4Address(ipAddress);
}
return status;
}
/**
* Validates IPV6 address using regex for the given ipAddress
*
* @param ipAddress
* IP Address
* @return {@link Boolean} status flag
*/
private static Boolean isValidIPV6(final String ipAddress) {
boolean status = false;
if (StringUtils.isNotEmpty(ipAddress)) {
status = InetAddressUtils.isIPv6Address(ipAddress);
}
return status;
}
/**
* Validates if a label contains only alphanumeric values
*
* @param label
* Label to validate
* @return {@link Boolean} status flag
*/
private static Boolean isAlphanumeric(final String label) {
boolean status = false;
if (StringUtils.isNotEmpty(label)) {
Matcher matcher = patternAlphanumeric.matcher(label);
status = matcher.matches();
}
return status;
}
public static void checkFsName(final String fsName, final String fieldName) {
checkFieldNotEmpty(fsName, fieldName);
if (!isAlphanumeric(fsName)) {
throw APIException.badRequests.invalidFileshareName(fsName);
}
}
public static void checkQuotaDirName(final String quotaDirName, final String fieldName) {
checkFieldNotEmpty(quotaDirName, fieldName);
if (!isAlphanumeric(quotaDirName)) {
throw APIException.badRequests.invalidQuotaDirName(quotaDirName);
}
}
/**
* Validates that a named field is of minimum or greater value.
*
* @param value
* the suppled number to check
* @param minimum
* the minimum acceptable value
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldMinimum(final long value, final long minimum, final String fieldName) {
checkFieldMinimum(value, minimum, "", fieldName);
}
/**
* Validates that a named field is of minimum or greater value.
*
* @param value
* the suppled number to check
* @param minimum
* the minimum acceptable value
* @param units
* the units that the value represents, used for error message presentation
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldMinimum(final long value, final long minimum, final String units, final String fieldName) {
if (value < minimum) {
throw APIException.badRequests.invalidParameterBelowMinimum(fieldName, value, minimum, units);
}
}
/**
* Validates that a named field is of maximum or lesser value.
*
* @param value
* the suppled number to check
* @param maximum
* the maximum acceptable value
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldMaximum(final long value, final long maximum, final String fieldName) {
checkFieldMaximum(value, maximum, "", fieldName);
}
/**
* Validates that a named field is of maximum or lesser value.
*
* @param value
* the supplied number to check
* @param maximum
* the maximum acceptable value
* @param units
* the units that the value represents, used for error message presentation
* @param fieldName
* the name of the field where the value originated
* @param humanReadableError
* if true, the error will show simplified and user friendly units in the error message
*/
public static void checkFieldMaximum(final long value, final long maximum, final String units, final String fieldName,
final Boolean humanReadableError) {
if (value > maximum) {
if (humanReadableError) {
throw APIException.badRequests.invalidParameterSizeAboveMaximum(fieldName,
SizeUtil.humanReadableByteCount(SizeUtil.translateSizeToBytes(value - maximum, units)),
SizeUtil.humanReadableByteCount(SizeUtil.translateSizeToBytes(maximum, units)));
} else {
checkFieldMaximum(value, maximum, units, fieldName);
}
}
}
/**
* Validates that a named field is of maximum or lesser value.
*
* @param value
* the suppled number to check
* @param maximum
* the maximum acceptable value
* @param units
* the units that the value represents, used for error message presentation
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldMaximum(final long value, final long maximum, final String units, final String fieldName) {
if (value > maximum) {
throw APIException.badRequests.invalidParameterAboveMaximum(fieldName, value, maximum, (" " + units));
}
}
/**
* Validates that a named field is within the inclusive range specified.
*
* @param value
* the suppled number to check
* @param minimum
* the minimum acceptable value
* @param maximum
* the maximum acceptable value
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldRange(final long value, final long minimum, final long maximum, final String fieldName) {
checkFieldRange(value, minimum, maximum, "", fieldName);
}
/**
* Validates that a named field is within the inclusive range specified.
*
* @param value
* the suppled number to check
* @param minimum
* the minimum acceptable value
* @param maximum
* the maximum acceptable value
* @param units
* the units that the value represents, used for error message presentation
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldRange(final long value, final long minimum, final long maximum, final String units,
final String fieldName) {
if (value < minimum || value > maximum) {
throw APIException.badRequests.parameterNotWithinRange(fieldName, value, minimum, maximum, units);
}
}
/**
* Validates that a named field is of maximum or lesser value.
*
* @param value
* the suppled value to check
* @param maximum
* the maximum acceptable value length
* @param fieldName
* the name of the field where the value originated
*/
public static void checkFieldLengthMaximum(final String value, final long maximum, final String fieldName) {
if (value.length() > maximum) {
throw APIException.badRequests.invalidParameterLengthTooLong(fieldName, value, maximum);
}
}
/**
* @deprecated raise a specific exception or use a more specific utility, do not use this method with unlocalized
* message and parameters
*/
@Deprecated
public static void checkArgument(final boolean condition, final String pattern, final Object... parameters) {
if (!condition) {
throw new ServiceCodeException(ServiceCode.API_BAD_PARAMETERS, pattern, parameters);
}
}
/**
* @deprecated raise a specific exception or use a more specific utility, do not use this method with an unlocalized
* message
*/
@Deprecated
public static void checkNotNull(final Object value, final String message) {
checkArgument(value != null, message);
}
/**
* Check the provided String value is a valid type of enum or not
*
* @param value
* String need to be checked
* @param enumClass
* the enum class for which it need to be checked.
* @return true/false
*/
public static <T extends Enum<T>> boolean isValidEnum(String value, Class<T> enumClass) {
for (T e : enumClass.getEnumConstants()) {
if (e.name().equalsIgnoreCase(value)) {
return true;
}
}
return false;
}
/**
* Check the format of the endpoint wwn entered.
*
* @param wwn
* wwn field
*/
public static void checkFieldValidWwn(String wwn) {
if (!WwnUtils.isValidEndpoint(wwn)) {
throw APIException.badRequests.invalidParameterWwnBadFormat(wwn);
}
}
/**
* Check whether consistency group has special characters other than _ and -
*
* @param consistencyGroupName
*/
public static void checkIsAlphaNumeric(String consistencyGroupName) {
if (!consistencyGroupName.matches(ALPHA_NUMERIC_UNDERSCORE)) {
throw APIException.badRequests.groupNameonlyAlphaNumericAllowed();
}
}
/**
* Check whether the field is not strictly numeric.
* Basic check since some fields in compute folks are entering VLAN IDs and
* not an IP address or FQDN.
*
* Note: the error is specific to IP addresses, so don't use this unless you're
* validating an IP/FQDN field.
*
* @param ip field to validate
* @param fieldName the name of the field
*/
public static void checkIpIsNotNumeric(final String ip, final String fieldName) {
checkFieldNotEmpty(ip, fieldName);
if (ip.matches(NUMERIC_PATTERN)) {
throw APIException.badRequests.numberNotAllowed();
}
}
}