private static void walkObjectTree(final Object object,
                                       final Predicate<Prop> propFilter,
                                       final Consumer<Prop> propConsumer,
                                       final String indent) {

        // Only care about stroom pojos
        final Map<String, Prop> propMap = getProperties(object);
//        System.out.println(object.getClass().getSimpleName() + " => " + String.join(", ", propMap.keySet()));

        propMap.values().stream()
                .filter(propFilter)
                .forEach(prop -> {
                    LOGGER.trace("{}{}#{}",
                            indent,
                            object.getClass().getSimpleName(),
                            prop.getName());

                    // process the prop
                    propConsumer.accept(prop);
                    final Object childValue;
                    try {
                        childValue = prop.getValueFromConfigObject();
                    } catch (Exception e) {
                        LOGGER.error("Error getting value for prop {}, object {}", prop, object, e);
                        throw e;
                    }
                    if (childValue == null) {
                        LOGGER.trace("{}Null value", indent + "  ");
                    } else if (childValue instanceof Enum<?>) {
                        // We don't want to recurse into enums
                        LOGGER.trace(() -> LogUtil.message("{}Ignoring Enum value of type {}",
                                indent + "  ",
                                childValue.getClass().getSimpleName()));
                    } else if (childValue instanceof Collection<?>) {
                        // We don't want to recurse into collection types
                        LOGGER.trace(() -> LogUtil.message("{}Ignoring Collection value of type {}",
                                indent + "  ",
                                childValue.getClass().getSimpleName()));
                    } else {
                        // descend into the prop, which may or may not have its own props
                        walkObjectTree(
                                childValue,
                                propFilter,
                                propConsumer,
                                indent + "  ");
                    }
                });
    }
Walks the public properties of the supplied object, passing each property to the
property consumer if it passes the filter test.
stroom-util/src/main/java/stroom/util/config/PropertyUtil.java