Skip to content

Commit

Permalink
BEANUTILS-541 - FluentPropertyBeanIntrospector caches corrupted write…
Browse files Browse the repository at this point in the history
…Method (parallel) (#234)

* BEANUTILS-541 - FluentPropertyBeanIntrospector caches corrupted writeMethod (parallel)

* Fix import order (Checkstyle)

---------

Co-authored-by: Gary Gregory <[email protected]>
  • Loading branch information
seregamorph and garydgregory authored Apr 14, 2024
1 parent b7a4700 commit a726f85
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,13 @@ public void introspect(final IntrospectionContext icontext)
icontext.addPropertyDescriptor(createFluentPropertyDescritor(
m, propertyName));
} else if (pd.getWriteMethod() == null) {
// We change statically cached PropertyDescriptor, it may affect
// other subclasses of targetClass supertype.
// We should not change statically cached PropertyDescriptor as it can be from super-type,
// it may affect other subclasses of targetClass supertype.
// See BEANUTILS-541 for more details.
clearDescriptorsCacheHierarchy(icontext.getTargetClass().getSuperclass());
pd.setWriteMethod(m);
PropertyDescriptor fluentPropertyDescriptor = new PropertyDescriptor(
pd.getName(), pd.getReadMethod(), m);
// replace existing (possibly inherited from super-class) to one specific to current class
icontext.addPropertyDescriptor(fluentPropertyDescriptor);
}
} catch (final IntrospectionException e) {
if (log.isDebugEnabled()) {
Expand All @@ -170,13 +172,6 @@ public void introspect(final IntrospectionContext icontext)
}
}

private static void clearDescriptorsCacheHierarchy(Class<?> cls) {
if (cls != null && cls != Object.class) {
Introspector.flushFromCaches(cls);
clearDescriptorsCacheHierarchy(cls.getSuperclass());
}
}

/**
* Derives the name of a property from the given set method.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
*/
package org.apache.commons.beanutils2.bugs;

import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.beanutils2.FluentPropertyBeanIntrospector;
import org.apache.commons.beanutils2.PropertyUtilsBean;
Expand All @@ -34,6 +40,29 @@ public class Jira541TestCase {

@Test
public void testFluentBeanIntrospectorOnOverriddenSetter() throws Exception {
testImpl();
}

@Test
public void testFluentBeanIntrospectorOnOverriddenSetterConcurrent() throws Exception {
ExecutorService executionService = Executors.newFixedThreadPool(256);
try {
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
futures.add(executionService.submit(() -> {
testImpl();
return null;
}));
}
for (Future<?> future : futures) {
future.get();
}
} finally {
executionService.shutdown();
}
}

private static void testImpl() throws ReflectiveOperationException {
PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
propertyUtilsBean.addBeanIntrospector(new FluentPropertyBeanIntrospector());

Expand Down

0 comments on commit a726f85

Please sign in to comment.