public static <T> ObjectInfo<T> getObjectInfo(final ObjectMapper objectMapper,
                                                  final String name,
                                                  final T object) {
        Objects.requireNonNull(object);
        LOGGER.trace("getProperties called for {}", object);
        final Map<String, Prop> propMap = new HashMap<>();

        final Class<T> clazz = (Class<T>) object.getClass();

        final JavaType userType = objectMapper.getTypeFactory().constructType(object.getClass());
        final BeanDescription beanDescription =
                objectMapper.getSerializationConfig().introspect(userType);

        final List<BeanPropertyDefinition> props = beanDescription.findProperties();
        final Set<String> propNames = props.stream()
                .map(BeanPropertyDefinition::getName)
                .collect(Collectors.toSet());

        // Now filter out all the prop objects that are not pojo props with getter+setter
        final Map<String, Prop> propertyMap = props
                .stream()
                .map(propDef -> {
                    final String propName = propDef.getName();

                    final Prop prop = new Prop(propName, object);
                    if (propDef.getField() != null) {
                        propDef.getField()
                                .getAllAnnotations()
                                .annotations()
                                .forEach(prop::addFieldAnnotation);
                    }
                    if (propDef.hasGetter()) {
                        prop.setGetter(propDef.getGetter().getAnnotated());
                    }
                    if (propDef.hasSetter()) {
                        prop.setSetter(propDef.getSetter().getAnnotated());
                    }

                    LOGGER.trace(() -> LogUtil.message(
                            "name: {}, hasGetter: {}, hasSetter: {}, field annotations: {}, getter annotations: {}",
                            prop.name,
                            prop.getter != null,
                            prop.setter != null,
                            prop.getFieldAnnotations(),
                            prop.getGetterAnnotations()));

                    return prop;
                })
                .collect(Collectors.toMap(Prop::getName, Function.identity()));

        final Optional<Constructor<T>> optJsonCreatorConstructor = getJsonCreatorConstructor(beanDescription);
        final List<String> constructorArgNames = optJsonCreatorConstructor
                .map(PropertyUtil::getConstructorArgNames)
                .orElseGet(Collections::emptyList);

        constructorArgNames.forEach(argName -> {
            if (!propNames.contains(argName)) {
                throw new RuntimeException(LogUtil.message(
                        "No matching property found for constructor property {} on {}", argName, object));
            }
        });

        return new ObjectInfo<>(
                name,
                clazz,
                propertyMap,
                constructorArgNames,
                optJsonCreatorConstructor.orElse(null));
    }
Builds a map of property name to {@link Prop} object that provides access to the getter/setter.
Only includes public properties, not package private
stroom-util/src/main/java/stroom/util/config/PropertyUtil.java