BEANUTILS-541 - FluentPropertyBeanIntrospector caches corrupted writeMethod (#68)

diff --git a/pom.xml b/pom.xml
index 012e3f4..90484c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -539,5 +539,9 @@
       <name>Raviteja Lokineni</name>
       <email />
     </contributor>
+    <contributor>
+      <name>Sergey Chernov</name>
+      <email></email>
+    </contributor>
   </contributors>
 </project>
diff --git a/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java b/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java
index 93c15ae..21eb417 100644
--- a/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java
+++ b/src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java
@@ -154,6 +154,10 @@
                         icontext.addPropertyDescriptor(createFluentPropertyDescritor(
                                 m, propertyName));
                     } else if (pd.getWriteMethod() == null) {
+                        // We change statically cached PropertyDescriptor, it may affect
+                        // other subclasses of targetClass supertype.
+                        // See BEANUTILS-541 for more details.
+                        clearDescriptorsCacheHierarchy(icontext.getTargetClass().getSuperclass());
                         pd.setWriteMethod(m);
                     }
                 } catch (final IntrospectionException e) {
@@ -166,6 +170,13 @@
         }
     }
 
+    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.
      *
diff --git a/src/test/java/org/apache/commons/beanutils2/bugs/Jira541TestCase.java b/src/test/java/org/apache/commons/beanutils2/bugs/Jira541TestCase.java
new file mode 100644
index 0000000..a8fac5d
--- /dev/null
+++ b/src/test/java/org/apache/commons/beanutils2/bugs/Jira541TestCase.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.beanutils2.bugs;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.commons.beanutils2.FluentPropertyBeanIntrospector;
+import org.apache.commons.beanutils2.PropertyUtilsBean;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Fix BEANUTILS-541
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/BEANUTILS-541">https://issues.apache.org/jira/browse/BEANUTILS-541</a>
+ */
+public class Jira541TestCase {
+
+    private static final String FIELD_NAME = "field";
+    private static final String FIELD_VALUE = "name";
+
+    @Test
+    public void testFluentBeanIntrospectorOnOverriddenSetter() throws Exception {
+        PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
+        propertyUtilsBean.addBeanIntrospector(new FluentPropertyBeanIntrospector());
+
+        // note: we should setProperty first on SubTypeA (with overridden setter), then on subTypeB
+        // but not vice versa
+        SubTypeA subTypeA = new SubTypeA();
+        propertyUtilsBean.setProperty(subTypeA, FIELD_NAME, FIELD_VALUE);
+
+        SubTypeB subTypeB = new SubTypeB();
+        propertyUtilsBean.setProperty(subTypeB, FIELD_NAME, FIELD_VALUE);
+
+        assertEquals(FIELD_VALUE, subTypeA.getField());
+        assertEquals(FIELD_VALUE, subTypeB.getField());
+    }
+
+    public static class BaseType {
+
+        private String field;
+
+        public BaseType setField(String objectName) {
+            this.field = objectName;
+            return this;
+        }
+
+        public String getField() {
+            return field;
+        }
+    }
+
+    public static class SubTypeA extends BaseType {
+
+        @Override
+        public SubTypeA setField(String field) {
+            super.setField(field);
+            return this;
+        }
+    }
+
+    public static class SubTypeB extends BaseType {
+
+    }
+}