use of org.apache.beam.sdk.PipelineRunner in project beam by apache.
the class PipelineOptionsFactory method parseObjects.
/**
* Using the parsed string arguments, we convert the strings to the expected return type of the
* methods that are found on the passed-in class.
*
* <p>For any return type that is expected to be an array or a collection, we further split up
* each string on ','.
*
* <p>We special case the "runner" option. It is mapped to the class of the {@link PipelineRunner}
* based off of the {@link PipelineRunner PipelineRunners} simple class name. If the provided
* runner name is not registered via a {@link PipelineRunnerRegistrar}, we attempt to obtain the
* class that the name represents using {@link Class#forName(String)} and use the result class if
* it subclasses {@link PipelineRunner}.
*
* <p>If strict parsing is enabled, unknown options or options that cannot be converted to the
* expected java type using an {@link ObjectMapper} will be ignored.
*/
private static <T extends PipelineOptions> Map<String, Object> parseObjects(Class<T> klass, ListMultimap<String, String> options, boolean strictParsing) {
Map<String, Method> propertyNamesToGetters = Maps.newHashMap();
Cache cache = CACHE.get();
cache.validateWellFormed(klass);
@SuppressWarnings("unchecked") Iterable<PropertyDescriptor> propertyDescriptors = cache.getPropertyDescriptors(Stream.concat(getRegisteredOptions().stream(), Stream.of(klass)).collect(Collectors.toSet()));
for (PropertyDescriptor descriptor : propertyDescriptors) {
propertyNamesToGetters.put(descriptor.getName(), descriptor.getReadMethod());
}
Map<String, Object> convertedOptions = Maps.newHashMap();
for (final Map.Entry<String, Collection<String>> entry : options.asMap().entrySet()) {
try {
// Either off by one or off by two character errors.
if (!propertyNamesToGetters.containsKey(entry.getKey())) {
SortedSet<String> closestMatches = new TreeSet<>(Sets.filter(propertyNamesToGetters.keySet(), input -> StringUtils.getLevenshteinDistance(entry.getKey(), input) <= 2));
switch(closestMatches.size()) {
case 0:
throw new IllegalArgumentException(String.format("Class %s missing a property named '%s'.", klass, entry.getKey()));
case 1:
throw new IllegalArgumentException(String.format("Class %s missing a property named '%s'. Did you mean '%s'?", klass, entry.getKey(), Iterables.getOnlyElement(closestMatches)));
default:
throw new IllegalArgumentException(String.format("Class %s missing a property named '%s'. Did you mean one of %s?", klass, entry.getKey(), closestMatches));
}
}
Method method = propertyNamesToGetters.get(entry.getKey());
// Only allow empty argument values for String, String Array, and Collection<String>.
Class<?> returnType = method.getReturnType();
JavaType type = MAPPER.getTypeFactory().constructType(method.getGenericReturnType());
if ("runner".equals(entry.getKey())) {
String runner = Iterables.getOnlyElement(entry.getValue());
final Map<String, Class<? extends PipelineRunner<?>>> pipelineRunners = cache.supportedPipelineRunners;
if (pipelineRunners.containsKey(runner.toLowerCase())) {
convertedOptions.put("runner", pipelineRunners.get(runner.toLowerCase(ROOT)));
} else {
try {
Class<?> runnerClass = Class.forName(runner, true, ReflectHelpers.findClassLoader());
if (!PipelineRunner.class.isAssignableFrom(runnerClass)) {
throw new IllegalArgumentException(String.format("Class '%s' does not implement PipelineRunner. " + "Supported pipeline runners %s", runner, cache.getSupportedRunners()));
}
convertedOptions.put("runner", runnerClass);
} catch (ClassNotFoundException e) {
String msg = String.format("Unknown 'runner' specified '%s', supported pipeline runners %s", runner, cache.getSupportedRunners());
throw new IllegalArgumentException(msg, e);
}
}
} else if (isCollectionOrArrayOfAllowedTypes(returnType, type)) {
// Split any strings with ","
List<String> values = entry.getValue().stream().flatMap(input -> Arrays.stream(input.split(","))).collect(Collectors.toList());
if (values.contains("")) {
checkEmptyStringAllowed(returnType, type, method.getGenericReturnType().toString());
}
convertedOptions.put(entry.getKey(), MAPPER.convertValue(values, type));
} else if (isSimpleType(returnType, type)) {
String value = Iterables.getOnlyElement(entry.getValue());
if (value.isEmpty()) {
checkEmptyStringAllowed(returnType, type, method.getGenericReturnType().toString());
}
convertedOptions.put(entry.getKey(), MAPPER.convertValue(value, type));
} else {
String value = Iterables.getOnlyElement(entry.getValue());
if (value.isEmpty()) {
checkEmptyStringAllowed(returnType, type, method.getGenericReturnType().toString());
}
try {
convertedOptions.put(entry.getKey(), tryParseObject(value, method));
} catch (IOException e) {
throw new IllegalArgumentException("Unable to parse JSON value " + value, e);
}
}
} catch (IllegalArgumentException e) {
if (strictParsing) {
throw e;
} else {
LOG.warn("Strict parsing is disabled, ignoring option '{}' with value '{}' because {}", entry.getKey(), entry.getValue(), e.getMessage());
}
}
}
return convertedOptions;
}
Aggregations