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