Skip to content

Commit 54a3d04

Browse files
authored
Do not resolve suppressed mapped properties in getPropertyDescriptor (#413)
1 parent 088e3cd commit 54a3d04

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,13 @@ public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws
675675
return result;
676676
}
677677

678+
// A name removed by a SuppressPropertiesBeanIntrospector is absent from data above, but the
679+
// mapped-descriptor fallback below rebuilds descriptors straight from the class methods and never
680+
// consults the introspectors, so a suppressed mapped property would still be resolved. Keep it hidden.
681+
if (isPropertySuppressed(name)) {
682+
return null;
683+
}
684+
678685
Map mappedDescriptors = getMappedPropertyDescriptors(bean);
679686
if (mappedDescriptors == null) {
680687
mappedDescriptors = new ConcurrentHashMap<Class<?>, Map>();
@@ -698,6 +705,23 @@ public PropertyDescriptor getPropertyDescriptor(Object bean, String name) throws
698705
return result;
699706
}
700707

708+
/**
709+
* Tests whether a property name has been removed by a registered {@link SuppressPropertiesBeanIntrospector}. The mapped-descriptor fallback in
710+
* {@link #getPropertyDescriptor(Object, String)} bypasses the introspection pipeline, so suppressed mapped property names must be filtered explicitly.
711+
*
712+
* @param name The property name to test.
713+
* @return {@code true} if the name is suppressed by an introspector.
714+
*/
715+
private boolean isPropertySuppressed(final String name) {
716+
for (final BeanIntrospector introspector : introspectors) {
717+
if (introspector instanceof SuppressPropertiesBeanIntrospector
718+
&& ((SuppressPropertiesBeanIntrospector) introspector).getSuppressedProperties().contains(name)) {
719+
return true;
720+
}
721+
}
722+
return false;
723+
}
724+
701725
/**
702726
* <p>
703727
* Retrieve the property descriptors for the specified class, introspecting and caching them the first time a particular bean class is encountered.

src/test/java/org/apache/commons/beanutils2/PropertyUtilsTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,27 @@ void testCustomIntrospectionEx() {
294294
PropertyUtils.removeBeanIntrospector(bi);
295295
}
296296

297+
/**
298+
* A mapped property removed by a {@link SuppressPropertiesBeanIntrospector} must stay hidden. The mapped-descriptor fallback in
299+
* {@code getPropertyDescriptor} rebuilt the descriptor directly from the class methods without consulting the introspectors, so a suppressed mapped
300+
* property was still readable and writable through {@code name(key)} access.
301+
*/
302+
@Test
303+
void testCustomIntrospectionSuppressedMappedProperty() throws Exception {
304+
final PropertyUtilsBean pub = new PropertyUtilsBean();
305+
pub.addBeanIntrospector(new SuppressPropertiesBeanIntrospector(Arrays.asList("mappedProperty")));
306+
307+
assertNull(pub.getPropertyDescriptor(bean, "mappedProperty"), "Suppressed mapped property should have no descriptor");
308+
assertThrows(NoSuchMethodException.class, () -> pub.getProperty(bean, "mappedProperty(First Key)"), "Suppressed mapped property must not be readable");
309+
assertThrows(NoSuchMethodException.class, () -> pub.setProperty(bean, "mappedProperty(First Key)", "changed"),
310+
"Suppressed mapped property must not be writable");
311+
assertEquals("First Value", bean.getMappedProperty("First Key"), "Suppressed mapped property must be unchanged");
312+
313+
// A mapped property that is not suppressed is still accessible.
314+
final PropertyUtilsBean unsuppressed = new PropertyUtilsBean();
315+
assertEquals("First Value", unsuppressed.getProperty(bean, "mappedProperty(First Key)"), "Mapped property should be readable when not suppressed");
316+
}
317+
297318
/**
298319
* Test the describe() method.
299320
*/

0 commit comments

Comments
 (0)