This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 0d4b396392db0362a8f1bc6a80c4f60005114f09
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Feb 22 15:52:51 2023 +0000

    A sub-set of JavaBeans support that does not depend on java.beans
    
    This for use by Expression Language when the java.desktop module (which
    is where the java.beans package resides) is not available.
---
 java/jakarta/el/BeanELResolver.java            |  76 ++------
 java/jakarta/el/BeanSupport.java               |  55 ++++++
 java/jakarta/el/BeanSupportFull.java           |  99 ++++++++++
 java/jakarta/el/BeanSupportStandalone.java     | 217 ++++++++++++++++++++++
 test/jakarta/el/BeanSupportBaseTest.java       | 244 +++++++++++++++++++++++++
 test/jakarta/el/TestBeanSupportFull.java       |  29 +++
 test/jakarta/el/TestBeanSupportStandalone.java |  34 ++++
 webapps/docs/changelog.xml                     |  11 ++
 8 files changed, 709 insertions(+), 56 deletions(-)

diff --git a/java/jakarta/el/BeanELResolver.java 
b/java/jakarta/el/BeanELResolver.java
index 80d46e171f..c661730ec3 100644
--- a/java/jakarta/el/BeanELResolver.java
+++ b/java/jakarta/el/BeanELResolver.java
@@ -16,10 +16,6 @@
  */
 package jakarta.el;
 
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.HashMap;
@@ -180,47 +176,13 @@ public class BeanELResolver extends ELResolver {
         return null;
     }
 
-    static final class BeanProperties {
-        private final Map<String, BeanProperty> properties;
-
-        private final Class<?> type;
+    abstract static class BeanProperties {
+        protected final Map<String, BeanProperty> properties;
+        protected final Class<?> type;
 
         BeanProperties(Class<?> type) throws ELException {
             this.type = type;
             this.properties = new HashMap<>();
-            try {
-                BeanInfo info = Introspector.getBeanInfo(this.type);
-                PropertyDescriptor[] pds = info.getPropertyDescriptors();
-                for (PropertyDescriptor pd : pds) {
-                    this.properties.put(pd.getName(), new BeanProperty(type, 
pd));
-                }
-                /*
-                 * Populating from any interfaces causes default methods to be 
included.
-                 */
-                populateFromInterfaces(type);
-            } catch (IntrospectionException ie) {
-                throw new ELException(ie);
-            }
-        }
-
-        private void populateFromInterfaces(Class<?> aClass) throws 
IntrospectionException {
-            Class<?> interfaces[] = aClass.getInterfaces();
-            if (interfaces.length > 0) {
-                for (Class<?> ifs : interfaces) {
-                    BeanInfo info = Introspector.getBeanInfo(ifs);
-                    PropertyDescriptor[] pds = info.getPropertyDescriptors();
-                    for (PropertyDescriptor pd : pds) {
-                        if (!this.properties.containsKey(pd.getName())) {
-                            this.properties.put(pd.getName(), new 
BeanProperty(this.type, pd));
-                        }
-                    }
-                    populateFromInterfaces(ifs);
-                }
-            }
-            Class<?> superclass = aClass.getSuperclass();
-            if (superclass != null) {
-                populateFromInterfaces(superclass);
-            }
         }
 
         private BeanProperty get(ELContext ctx, String name) {
@@ -236,21 +198,18 @@ public class BeanELResolver extends ELResolver {
         }
     }
 
-    static final class BeanProperty {
+    abstract static class BeanProperty {
         private final Class<?> type;
 
         private final Class<?> owner;
 
-        private final PropertyDescriptor descriptor;
-
         private Method read;
 
         private Method write;
 
-        BeanProperty(Class<?> owner, PropertyDescriptor descriptor) {
+        BeanProperty(Class<?> owner, Class<?> type) {
             this.owner = owner;
-            this.descriptor = descriptor;
-            this.type = descriptor.getPropertyType();
+            this.type = type;
         }
 
         public Class<?> getPropertyType() {
@@ -258,16 +217,15 @@ public class BeanELResolver extends ELResolver {
         }
 
         public boolean isReadOnly(Object base) {
-            return this.write == null &&
-                    (null == (this.write = Util.getMethod(this.owner, base, 
descriptor.getWriteMethod())));
+            return this.write == null && (null == (this.write = 
Util.getMethod(this.owner, base, getWriteMethod())));
         }
 
         private Method write(ELContext ctx, Object base) {
             if (this.write == null) {
-                this.write = Util.getMethod(this.owner, base, 
descriptor.getWriteMethod());
+                this.write = Util.getMethod(this.owner, base, 
getWriteMethod());
                 if (this.write == null) {
-                    throw new PropertyNotWritableException(Util.message(ctx, 
"propertyNotWritable",
-                            new Object[] { owner.getName(), 
descriptor.getName() }));
+                    throw new PropertyNotWritableException(
+                            Util.message(ctx, "propertyNotWritable", new 
Object[] { owner.getName(), getName() }));
                 }
             }
             return this.write;
@@ -275,14 +233,20 @@ public class BeanELResolver extends ELResolver {
 
         private Method read(ELContext ctx, Object base) {
             if (this.read == null) {
-                this.read = Util.getMethod(this.owner, base, 
descriptor.getReadMethod());
+                this.read = Util.getMethod(this.owner, base, getReadMethod());
                 if (this.read == null) {
-                    throw new PropertyNotFoundException(Util.message(ctx, 
"propertyNotReadable",
-                            new Object[] { owner.getName(), 
descriptor.getName() }));
+                    throw new PropertyNotFoundException(
+                            Util.message(ctx, "propertyNotReadable", new 
Object[] { owner.getName(), getName() }));
                 }
             }
             return this.read;
         }
+
+        abstract Method getWriteMethod();
+
+        abstract Method getReadMethod();
+
+        abstract String getName();
     }
 
     private BeanProperty property(ELContext ctx, Object base, Object property) 
{
@@ -291,7 +255,7 @@ public class BeanELResolver extends ELResolver {
 
         BeanProperties props = this.cache.get(type.getName());
         if (props == null || type != props.getType()) {
-            props = new BeanProperties(type);
+            props = BeanSupport.getInstance().getBeanProperties(type);
             this.cache.put(type.getName(), props);
         }
 
diff --git a/java/jakarta/el/BeanSupport.java b/java/jakarta/el/BeanSupport.java
new file mode 100644
index 0000000000..fdd60e596e
--- /dev/null
+++ b/java/jakarta/el/BeanSupport.java
@@ -0,0 +1,55 @@
+/*
+ * 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 jakarta.el;
+
+import jakarta.el.BeanELResolver.BeanProperties;
+
+/*
+ * Provides an abstraction so the BeanELResolver can obtain JavaBeans 
specification support via different
+ * implementations.
+ */
+abstract class BeanSupport {
+
+    private static final BeanSupport beanSupport;
+
+    static {
+        boolean useFull = 
!Boolean.getBoolean("jakarta.el.BeanSupport.useStandalone");
+
+        if (useFull) {
+            // If not explicitly configured to use standalone, use the full 
implementation unless it is not available.
+            try {
+                Class.forName("java.beans.BeanInfo");
+            } catch (Exception e) {
+                // Ignore: Expected if using modules and java.desktop module 
is not present
+                useFull = false;
+            }
+        }
+        if (useFull) {
+            // The full implementation provided by the java.beans package
+            beanSupport = new BeanSupportFull();
+        } else {
+            // The cut-down local implementation that does not depend on the 
java.beans package
+            beanSupport = new BeanSupportStandalone();
+        }
+    }
+
+    static BeanSupport getInstance() {
+        return beanSupport;
+    }
+
+    abstract BeanProperties getBeanProperties(Class<?> type);
+}
diff --git a/java/jakarta/el/BeanSupportFull.java 
b/java/jakarta/el/BeanSupportFull.java
new file mode 100644
index 0000000000..03770b56ab
--- /dev/null
+++ b/java/jakarta/el/BeanSupportFull.java
@@ -0,0 +1,99 @@
+/*
+ * 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 jakarta.el;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+
+import jakarta.el.BeanELResolver.BeanProperties;
+import jakarta.el.BeanELResolver.BeanProperty;
+
+class BeanSupportFull extends BeanSupport {
+
+    @Override
+    BeanProperties getBeanProperties(Class<?> type) {
+        return new BeanPropertiesFull(type);
+    }
+
+    static final class BeanPropertiesFull extends BeanProperties {
+
+        BeanPropertiesFull(Class<?> type) throws ELException {
+            super(type);
+            try {
+                BeanInfo info = Introspector.getBeanInfo(this.type);
+                PropertyDescriptor[] pds = info.getPropertyDescriptors();
+                for (PropertyDescriptor pd : pds) {
+                    this.properties.put(pd.getName(), new 
BeanPropertyFull(type, pd));
+                }
+                /*
+                 * Populating from any interfaces causes default methods to be 
included.
+                 */
+                populateFromInterfaces(type);
+            } catch (IntrospectionException ie) {
+                throw new ELException(ie);
+            }
+        }
+
+        private void populateFromInterfaces(Class<?> aClass) throws 
IntrospectionException {
+            Class<?> interfaces[] = aClass.getInterfaces();
+            if (interfaces.length > 0) {
+                for (Class<?> ifs : interfaces) {
+                    BeanInfo info = Introspector.getBeanInfo(ifs);
+                    PropertyDescriptor[] pds = info.getPropertyDescriptors();
+                    for (PropertyDescriptor pd : pds) {
+                        if (!this.properties.containsKey(pd.getName())) {
+                            this.properties.put(pd.getName(), new 
BeanPropertyFull(this.type, pd));
+                        }
+                    }
+                    populateFromInterfaces(ifs);
+                }
+            }
+            Class<?> superclass = aClass.getSuperclass();
+            if (superclass != null) {
+                populateFromInterfaces(superclass);
+            }
+        }
+    }
+
+    static final class BeanPropertyFull extends BeanProperty {
+
+        private final PropertyDescriptor descriptor;
+
+        BeanPropertyFull(Class<?> owner, PropertyDescriptor descriptor) {
+            super(owner, descriptor.getPropertyType());
+            this.descriptor = descriptor;
+        }
+
+        @Override
+        Method getWriteMethod() {
+            return descriptor.getWriteMethod();
+        }
+
+        @Override
+        Method getReadMethod() {
+            return descriptor.getReadMethod();
+        }
+
+        @Override
+        String getName() {
+            return descriptor.getName();
+        }
+    }
+}
diff --git a/java/jakarta/el/BeanSupportStandalone.java 
b/java/jakarta/el/BeanSupportStandalone.java
new file mode 100644
index 0000000000..a2531451d2
--- /dev/null
+++ b/java/jakarta/el/BeanSupportStandalone.java
@@ -0,0 +1,217 @@
+/*
+ * 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 jakarta.el;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jakarta.el.BeanELResolver.BeanProperties;
+import jakarta.el.BeanELResolver.BeanProperty;
+
+/*
+ * Implements those parts of the JavaBeans Specification that can be 
implemented without reference to the java.beans
+ * package.
+ */
+class BeanSupportStandalone extends BeanSupport {
+
+    @Override
+    BeanProperties getBeanProperties(Class<?> type) {
+        return new BeanPropertiesStandalone(type);
+    }
+
+
+    private static PropertyDescriptor[] getPropertyDescriptors(Class<?> type) {
+        Map<String, PropertyDescriptor> pds = new HashMap<>();
+        Method[] methods = type.getMethods();
+        for (Method method : methods) {
+            if (!Modifier.isStatic(method.getModifiers())) {
+                String methodName = method.getName();
+                if (methodName.startsWith("is")) {
+                    if (method.getParameterCount() == 0 && 
method.getReturnType() == boolean.class) {
+                        String propertyName = 
getPropertyName(methodName.substring(2));
+                        PropertyDescriptor pd = 
pds.computeIfAbsent(propertyName, k -> new PropertyDescriptor());
+                        pd.setName(propertyName);
+                        pd.setReadMethodIs(method);
+                    }
+                } else if (methodName.startsWith("get")) {
+                    if (method.getParameterCount() == 0) {
+                        String propertyName = 
getPropertyName(methodName.substring(3));
+                        PropertyDescriptor pd = 
pds.computeIfAbsent(propertyName, k -> new PropertyDescriptor());
+                        pd.setName(propertyName);
+                        pd.setReadMethod(method);
+                    }
+                } else if (methodName.startsWith("set")) {
+                    if (method.getParameterCount() == 1 && 
method.getReturnType() == void.class) {
+                        String propertyName = 
getPropertyName(methodName.substring(3));
+                        PropertyDescriptor pd = 
pds.computeIfAbsent(propertyName, k -> new PropertyDescriptor());
+                        pd.setName(propertyName);
+                        pd.addWriteMethod(method);
+                    }
+
+                }
+            }
+        }
+        return pds.values().toArray(new PropertyDescriptor[0]);
+    }
+
+
+    private static String getPropertyName(String input) {
+        if (input.length() == 0) {
+            return null;
+        }
+        if (!Character.isUpperCase(input.charAt(0))) {
+            return null;
+        }
+        if (input.length() > 1 && Character.isUpperCase(input.charAt(1))) {
+            return input;
+        }
+        char[] chars = input.toCharArray();
+        chars[0] = Character.toLowerCase(chars[0]);
+        return new String(chars);
+    }
+
+
+    private static class PropertyDescriptor {
+        private String name;
+        private boolean usesIs;
+        private Method readMethod;
+        private Method writeMethod;
+        private List<Method> writeMethods = new ArrayList<>();
+
+        String getName() {
+            return name;
+        }
+
+        void setName(String name) {
+            this.name = name;
+        }
+
+        Class<?> getType() {
+            if (readMethod == null) {
+                return getWriteMethod().getParameterTypes()[0];
+            }
+            return readMethod.getReturnType();
+        }
+
+        Method getReadMethod() {
+            return readMethod;
+        }
+
+        void setReadMethod(Method readMethod) {
+            if (usesIs) {
+                return;
+            }
+            this.readMethod = readMethod;
+        }
+
+        void setReadMethodIs(Method readMethod) {
+            this.readMethod = readMethod;
+            this.usesIs = true;
+        }
+
+        Method getWriteMethod() {
+            if (writeMethod == null) {
+                Class<?> type;
+                if (readMethod != null) {
+                    type = readMethod.getReturnType();
+                } else {
+                    type = writeMethods.get(0).getParameterTypes()[0];
+                }
+                for (Method candidate : writeMethods) {
+                    if 
(type.isAssignableFrom(candidate.getParameterTypes()[0])) {
+                        type = candidate.getParameterTypes()[0];
+                        this.writeMethod = candidate;
+                    }
+                }
+            }
+            return writeMethod;
+        }
+
+        void addWriteMethod(Method writeMethod) {
+            this.writeMethods.add(writeMethod);
+        }
+    }
+
+
+    static final class BeanPropertiesStandalone extends BeanProperties {
+
+        BeanPropertiesStandalone(Class<?> type) throws ELException {
+            super(type);
+            PropertyDescriptor[] pds = getPropertyDescriptors(this.type);
+            for (PropertyDescriptor pd : pds) {
+                this.properties.put(pd.getName(), new 
BeanPropertyStandalone(type, pd));
+            }
+            /*
+             * Populating from any interfaces causes default methods to be 
included.
+             */
+            populateFromInterfaces(type);
+        }
+
+        private void populateFromInterfaces(Class<?> aClass) {
+            Class<?> interfaces[] = aClass.getInterfaces();
+            if (interfaces.length > 0) {
+                for (Class<?> ifs : interfaces) {
+                    PropertyDescriptor[] pds = getPropertyDescriptors(type);
+                    for (PropertyDescriptor pd : pds) {
+                        if (!this.properties.containsKey(pd.getName())) {
+                            this.properties.put(pd.getName(), new 
BeanPropertyStandalone(this.type, pd));
+                        }
+                    }
+                    populateFromInterfaces(ifs);
+                }
+            }
+            Class<?> superclass = aClass.getSuperclass();
+            if (superclass != null) {
+                populateFromInterfaces(superclass);
+            }
+        }
+    }
+
+
+    static final class BeanPropertyStandalone extends BeanProperty {
+
+        private final String name;
+        private final Method readMethod;
+        private final Method writeMethod;
+
+        BeanPropertyStandalone(Class<?> owner, PropertyDescriptor pd) {
+            super(owner, pd.getType());
+            name = pd.getName();
+            readMethod = pd.getReadMethod();
+            writeMethod = pd.getWriteMethod();
+        }
+
+        @Override
+        String getName() {
+            return name;
+        }
+
+        @Override
+        Method getReadMethod() {
+            return readMethod;
+        }
+
+        @Override
+        Method getWriteMethod() {
+            return writeMethod;
+        }
+    }
+}
diff --git a/test/jakarta/el/BeanSupportBaseTest.java 
b/test/jakarta/el/BeanSupportBaseTest.java
new file mode 100644
index 0000000000..e594811052
--- /dev/null
+++ b/test/jakarta/el/BeanSupportBaseTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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 jakarta.el;
+
+import jakarta.el.BeanELResolver.BeanProperties;
+import jakarta.el.BeanELResolver.BeanProperty;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BeanSupportBaseTest {
+
+    @Test
+    public void testSimpleBean() {
+        doTest(SimpleBean.class, "value", TypeA.class, TypeA.class, 
TypeA.class);
+    }
+
+    @Test
+    public void testInvalidIs01Bean() {
+        doTest(InvalidIs01Bean.class, "value", TypeA.class, TypeA.class, 
TypeA.class);
+    }
+
+    @Test
+    public void testInvalidIs02Bean() {
+        doTest(InvalidIs02Bean.class, "value", TypeA.class, null, TypeA.class);
+    }
+
+    @Test
+    public void testInvalidIs03Bean() {
+        doTest(InvalidIs03Bean.class, "value", TypeA.class, null, TypeA.class);
+    }
+
+    @Test
+    public void testReadOnlyBean() {
+        doTest(ReadOnlyBean.class, "value", TypeA.class, TypeA.class, null);
+    }
+
+    @Test
+    public void testWriteOnlyBean() {
+        doTest(WriteOnlyBean.class, "value", TypeA.class, null, TypeA.class);
+    }
+
+    @Test
+    public void testOverLoadedWithGetABean() {
+        doTest(OverLoadedWithGetABean.class, "value", TypeA.class, 
TypeA.class, TypeAAA.class);
+    }
+
+    @Test
+    public void testOverLoadedWithGetAABean() {
+        doTest(OverLoadedWithGetAABean.class, "value", TypeAA.class, 
TypeAA.class, TypeAAA.class);
+    }
+
+    @Test
+    public void testOverLoadedWithGetAAABean() {
+        doTest(OverLoadedWithGetAAABean.class, "value", TypeAAA.class, 
TypeAAA.class, TypeAAA.class);
+    }
+
+    @Test
+    public void testMismatchBean() {
+        doTest(MismatchBean.class, "value", TypeA.class, TypeA.class, null);
+    }
+
+    /*
+     * The first setter found "wins".
+     */
+    @Test
+    public void testAmbiguousBean() {
+        doTest(AmbiguousBean.class, "value", TypeA.class, null, TypeA.class);
+    }
+
+
+    private void doTest(Class<?> clazz, String propertyName, Class<?> type, 
Class<?> typeGet, Class<?> typeSet) {
+        BeanProperties beanProperties = 
BeanSupport.getInstance().getBeanProperties(clazz);
+        BeanProperty beanProperty = 
beanProperties.properties.get(propertyName);
+
+        Assert.assertNotNull(beanProperty);
+        Assert.assertEquals(type, beanProperty.getPropertyType());
+
+        if (typeGet == null) {
+            Assert.assertNull(beanProperty.getReadMethod());
+        } else {
+            Assert.assertEquals(0, 
beanProperty.getReadMethod().getParameterCount());
+            Assert.assertEquals(typeGet, 
beanProperty.getReadMethod().getReturnType());
+        }
+
+        if (typeSet == null) {
+            Assert.assertNull(beanProperty.getWriteMethod());
+        } else {
+            Assert.assertEquals(void.class, 
beanProperty.getWriteMethod().getReturnType());
+            Assert.assertEquals(1, 
beanProperty.getWriteMethod().getParameterCount());
+            Assert.assertEquals(typeSet, 
beanProperty.getWriteMethod().getParameterTypes()[0]);
+        }
+    }
+
+
+    public static class SimpleBean {
+        public TypeA getValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+    }
+
+
+    public static class InvalidIs01Bean {
+        public TypeA isValue() {
+            return null;
+        }
+
+        public TypeA getValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+    }
+
+
+    public static class InvalidIs02Bean {
+        public TypeA isValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+    }
+
+
+    public static class InvalidIs03Bean {
+        public Boolean isValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+    }
+
+
+    public static class ReadOnlyBean {
+        public TypeA getValue() {
+            return null;
+        }
+    }
+
+
+    public static class WriteOnlyBean {
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+    }
+
+
+    public static class OverLoadedWithGetABean {
+        public TypeA getValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeAA value) {
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeAAA value) {
+        }
+    }
+
+
+    public static class OverLoadedWithGetAABean {
+        public TypeAA getValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeAA value) {
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeAAA value) {
+        }
+    }
+
+
+    public static class OverLoadedWithGetAAABean {
+        public TypeAAA getValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeAA value) {
+        }
+
+        public void setValue(@SuppressWarnings("unused") TypeAAA value) {
+        }
+    }
+
+
+    public static class MismatchBean {
+        public TypeA getValue() {
+            return null;
+        }
+
+        public void setValue(@SuppressWarnings("unused") String value) {
+        }
+    }
+
+
+    public static class AmbiguousBean {
+        public void setValue(@SuppressWarnings("unused") TypeA value) {
+        }
+
+        public void setValue(@SuppressWarnings("unused") String value) {
+        }
+    }
+
+
+    public static class TypeA {
+    }
+
+
+    public static class TypeAA extends TypeA {
+    }
+
+
+    public static class TypeAAA extends TypeAA {
+    }
+}
diff --git a/test/jakarta/el/TestBeanSupportFull.java 
b/test/jakarta/el/TestBeanSupportFull.java
new file mode 100644
index 0000000000..e8a5d1323c
--- /dev/null
+++ b/test/jakarta/el/TestBeanSupportFull.java
@@ -0,0 +1,29 @@
+/*
+ * 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 jakarta.el;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestBeanSupportFull extends BeanSupportBaseTest {
+
+    @Test
+    public void testImplementationClass() {
+        Assert.assertTrue(BeanSupport.getInstance() instanceof 
BeanSupportFull);
+    }
+
+}
diff --git a/test/jakarta/el/TestBeanSupportStandalone.java 
b/test/jakarta/el/TestBeanSupportStandalone.java
new file mode 100644
index 0000000000..ebb97a9158
--- /dev/null
+++ b/test/jakarta/el/TestBeanSupportStandalone.java
@@ -0,0 +1,34 @@
+/*
+ * 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 jakarta.el;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestBeanSupportStandalone extends BeanSupportBaseTest {
+
+    @Before
+    public void setup() {
+        System.setProperty("jakarta.el.BeanSupport.useStandalone", "true");
+    }
+
+    @Test
+    public void testImplementationClass() {
+        Assert.assertTrue(BeanSupport.getInstance() instanceof 
BeanSupportStandalone);
+    }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a09b12dde9..176b104c6c 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -114,6 +114,17 @@
       </update>
     </changelog>
   </subsection>
+  <subsection name="Jasper">
+    <changelog>
+      <add>
+        Provide an implementation of the sub-set of JavaBeans support that does
+        not depend on the <code>java.beans</code> package. This for use by
+        Expression Language when the <code>java.desktop</code> module (which is
+        where the <code>java.beans</code> package resides) is not available.
+        (markt)
+      </add>
+    </changelog>
+  </subsection>
 </section>
 <section name="Tomcat 11.0.0-M3 (markt)" rtext="release in progress">
   <subsection name="General">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to