in core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java [199:430]
private EndpointValidationResult validateProperties(
String scheme, Map<String, String> properties,
boolean lenient, boolean consumerOnly,
boolean producerOnly) {
EndpointValidationResult result = new EndpointValidationResult(scheme);
ComponentModel model = componentModel(scheme);
Map<String, BaseOptionModel> rows = new HashMap<>();
model.getComponentOptions().forEach(o -> rows.put(o.getName(), o));
// endpoint options have higher priority so overwrite component options
model.getEndpointOptions().forEach(o -> rows.put(o.getName(), o));
model.getEndpointPathOptions().forEach(o -> rows.put(o.getName(), o));
if (model.isApi()) {
String[] apiSyntax = StringHelper.splitWords(model.getApiSyntax());
String key = properties.get(apiSyntax[0]);
String key2 = apiSyntax.length > 1 ? properties.get(apiSyntax[1]) : null;
Map<String, BaseOptionModel> apiProperties = extractApiProperties(model, key, key2);
rows.putAll(apiProperties);
}
// the dataformat component refers to a data format so lets add the properties for the selected
// data format to the list of rows
if ("dataformat".equals(scheme)) {
String dfName = properties.get("name");
if (dfName != null) {
DataFormatModel dfModel = dataFormatModel(dfName);
if (dfModel != null) {
dfModel.getOptions().forEach(o -> rows.put(o.getName(), o));
}
}
}
for (Map.Entry<String, String> property : properties.entrySet()) {
String value = property.getValue();
String originalName = property.getKey();
// the name may be using an optional prefix, so lets strip that because the options
// in the schema are listed without the prefix
String name = stripOptionalPrefixFromName(rows, originalName);
// the name may be using a prefix, so lets see if we can find the real property name
String propertyName = getPropertyNameFromNameWithPrefix(rows, name);
if (propertyName != null) {
name = propertyName;
}
BaseOptionModel row = rows.get(name);
if (row == null) {
// unknown option
// only add as error if the component is not lenient properties, or not stub component
// and the name is not a property placeholder for one or more values
boolean namePlaceholder = name.startsWith("{{") && name.endsWith("}}");
if (!namePlaceholder && !"stub".equals(scheme)) {
if (lenient) {
// as if we are lenient then the option is a dynamic extra option which we cannot validate
result.addLenient(name);
} else {
// its unknown
result.addUnknown(name);
if (suggestionStrategy != null) {
String[] suggestions = suggestionStrategy.suggestEndpointOptions(rows.keySet(), name);
if (suggestions != null) {
result.addUnknownSuggestions(name, suggestions);
}
}
}
}
} else {
if ("parameter".equals(row.getKind())) {
// consumer only or producer only mode for parameters
String label = row.getLabel();
if (consumerOnly) {
if (label != null && label.contains("producer")) {
// the option is only for producer so you cannot use it in consumer mode
result.addNotConsumerOnly(name);
}
} else if (producerOnly) {
if (label != null && label.contains("consumer")) {
// the option is only for consumer so you cannot use it in producer mode
result.addNotProducerOnly(name);
}
}
}
String prefix = row.getPrefix();
boolean valuePlaceholder = value.startsWith("{{") || value.startsWith("${") || value.startsWith("$simple{");
boolean lookup = value.startsWith("#") && value.length() > 1;
// we cannot evaluate multi values as strict as the others, as we don't know their expected types
boolean multiValue = prefix != null && originalName.startsWith(prefix)
&& row.isMultiValue();
// default value
Object defaultValue = row.getDefaultValue();
if (defaultValue != null) {
result.addDefaultValue(name, defaultValue.toString());
}
// is required but the value is empty
if (row.isRequired() && CatalogHelper.isEmpty(value)) {
result.addRequired(name);
}
// is the option deprecated
boolean deprecated = row.isDeprecated();
if (deprecated) {
result.addDeprecated(name);
}
// is enum but the value is not within the enum range
// but we can only check if the value is not a placeholder
List<String> enums = row.getEnums();
if (!multiValue && !valuePlaceholder && !lookup && enums != null) {
boolean found = false;
for (String s : enums) {
String dashEC = StringHelper.camelCaseToDash(value);
String valueEC = StringHelper.asEnumConstantValue(value);
if (value.equalsIgnoreCase(s) || dashEC.equalsIgnoreCase(s) || valueEC.equalsIgnoreCase(s)) {
found = true;
break;
}
}
if (!found) {
handleNotFound(result, value, name, enums);
}
}
// is reference lookup of bean (not applicable for @UriPath, enums, or multi-valued)
if (!multiValue && enums == null && !"path".equals(row.getKind()) && "object".equals(row.getType())) {
// must start with # and be at least 2 characters
if (!value.startsWith("#") || value.length() <= 1) {
result.addInvalidReference(name, value);
}
}
// is boolean
if (!multiValue && !valuePlaceholder && !lookup && "boolean".equals(row.getType())) {
// value must be a boolean
boolean bool = ObjectHelper.isBoolean(value);
if (!bool) {
result.addInvalidBoolean(name, value);
}
}
// is duration
if (!multiValue && !valuePlaceholder && !lookup && "duration".equals(row.getType())) {
// value must be convertable to a duration
boolean valid = validateDuration(value);
if (!valid) {
result.addInvalidDuration(name, value);
}
}
// is integer
if (!multiValue && !valuePlaceholder && !lookup && "integer".equals(row.getType())) {
// value must be an integer
boolean valid = validateInteger(value);
if (!valid) {
result.addInvalidInteger(name, value);
}
}
// is number
if (!multiValue && !valuePlaceholder && !lookup && "number".equals(row.getType())) {
// value must be an number
boolean valid = false;
try {
valid = !Double.valueOf(value).isNaN() || !Float.valueOf(value).isNaN();
} catch (Exception e) {
// ignore
}
if (!valid) {
result.addInvalidNumber(name, value);
}
}
}
}
// for api component then check that the apiName/methodName combo is valid
if (model.isApi()) {
String[] apiSyntax = StringHelper.splitWords(model.getApiSyntax());
String key1 = properties.get(apiSyntax[0]);
String key2 = apiSyntax.length > 1 ? properties.get(apiSyntax[1]) : null;
if (key1 != null && key2 != null) {
ApiModel api = model.getApiOptions().stream().filter(o -> o.getName().equalsIgnoreCase(key1)).findFirst()
.orElse(null);
if (api == null) {
result.addInvalidEnum(apiSyntax[0], key1);
result.addInvalidEnumChoices(apiSyntax[0],
model.getApiOptions().stream().map(ApiModel::getName).toArray(String[]::new));
} else {
// walk each method and match against its name/alias
boolean found = false;
for (ApiMethodModel m : api.getMethods()) {
String key3 = apiMethodAlias(api, m);
if (m.getName().equalsIgnoreCase(key2) || key2.equalsIgnoreCase(key3)) {
found = true;
break;
}
}
if (!found) {
result.addInvalidEnum(apiSyntax[1], key2);
result.addInvalidEnumChoices(apiSyntax[1], api.getMethods().stream()
.map(m -> {
// favour using method alias in choices
String answer = apiMethodAlias(api, m);
if (answer == null) {
answer = m.getName();
}
return answer;
}).toArray(String[]::new));
}
}
}
}
// now check if all required values are there, and that a default value does not exist
for (BaseOptionModel row : rows.values()) {
if (row.isRequired()) {
String name = row.getName();
Object value = properties.get(name);
if (CatalogHelper.isEmpty(value)) {
value = row.getDefaultValue();
}
if (CatalogHelper.isEmpty(value)) {
result.addRequired(name);
}
}
}
return result;
}