Skip to content

Commit b9b280c

Browse files
authored
BEANUTILS-541 - FluentPropertyBeanIntrospector concurrency issue (backport 1.X) (#325)
1 parent e1efd7c commit b9b280c

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

src/main/java/org/apache/commons/beanutils/FluentPropertyBeanIntrospector.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,6 @@ public class FluentPropertyBeanIntrospector implements BeanIntrospector {
8080
/** The default prefix for write methods. */
8181
public static final String DEFAULT_WRITE_METHOD_PREFIX = "set";
8282

83-
private static void clearDescriptorsCacheHierarchy(final Class<?> cls) {
84-
if (cls != null && cls != Object.class) {
85-
Introspector.flushFromCaches(cls);
86-
clearDescriptorsCacheHierarchy(cls.getSuperclass());
87-
}
88-
}
89-
9083
/** The logger. */
9184
private final Log log = LogFactory.getLog(getClass());
9285

@@ -162,11 +155,13 @@ public void introspect(final IntrospectionContext icontext)
162155
icontext.addPropertyDescriptor(createFluentPropertyDescritor(
163156
m, propertyName));
164157
} else if (pd.getWriteMethod() == null) {
165-
// We change statically cached PropertyDescriptor, it may affect
166-
// other subclasses of targetClass supertype.
158+
// We should not change statically cached PropertyDescriptor as it can be from super-type,
159+
// it may affect other subclasses of targetClass supertype.
167160
// See BEANUTILS-541 for more details.
168-
clearDescriptorsCacheHierarchy(icontext.getTargetClass().getSuperclass());
169-
pd.setWriteMethod(m);
161+
PropertyDescriptor fluentPropertyDescriptor = new PropertyDescriptor(
162+
pd.getName(), pd.getReadMethod(), m);
163+
// replace existing (possibly inherited from super-class) to one specific to current class
164+
icontext.addPropertyDescriptor(fluentPropertyDescriptor);
170165
}
171166
} catch (final IntrospectionException e) {
172167
log.info("Error when creating PropertyDescriptor for " + m

src/test/java/org/apache/commons/beanutils/bugs/Jira541TestCase.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818

1919
import static org.junit.Assert.assertEquals;
2020

21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.concurrent.ExecutorService;
24+
import java.util.concurrent.Executors;
25+
import java.util.concurrent.Future;
26+
2127
import org.apache.commons.beanutils.FluentPropertyBeanIntrospector;
2228
import org.apache.commons.beanutils.PropertyUtilsBean;
2329
import org.junit.Test;
@@ -42,6 +48,7 @@ public BaseType setField(final String objectName) {
4248
return this;
4349
}
4450
}
51+
4552
public static class SubTypeA extends BaseType {
4653

4754
@Override
@@ -61,6 +68,29 @@ public static class SubTypeB extends BaseType {
6168

6269
@Test
6370
public void testFluentBeanIntrospectorOnOverriddenSetter() throws Exception {
71+
testImpl();
72+
}
73+
74+
@Test
75+
public void testFluentBeanIntrospectorOnOverriddenSetterConcurrent() throws Exception {
76+
ExecutorService executionService = Executors.newFixedThreadPool(256);
77+
try {
78+
List<Future<?>> futures = new ArrayList<>();
79+
for (int i = 0; i < 10000; i++) {
80+
futures.add(executionService.submit(() -> {
81+
testImpl();
82+
return null;
83+
}));
84+
}
85+
for (Future<?> future : futures) {
86+
future.get();
87+
}
88+
} finally {
89+
executionService.shutdown();
90+
}
91+
}
92+
93+
private void testImpl() throws Exception {
6494
final PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
6595
propertyUtilsBean.addBeanIntrospector(new FluentPropertyBeanIntrospector());
6696

0 commit comments

Comments
 (0)