This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 512c27ee6dafd9de63c39a68f7748ae6b465fc95 Author: opencode <[email protected]> AuthorDate: Tue Oct 20 11:46:14 2026 +0200 Report a bean property value which cannot be converted as a NamingException BeanFactory converts the value of each configured address of a resource reference to the type of the bean property which it names, using the conversion methods of the numeric types and, for a character property, the first character of the value. A value which does not match the type, such as a word for a property which is an integer, made one of those methods throw a NumberFormatException and an empty value for a character property made the reading of the first character throw a StringIndexOutOfBoundsException. As neither is one of the exceptions which the factory catches, it escaped the factory despite the NamingException which its contract promises for an error while the instance is created. The failure which reached the operator was then the unchecked exception alone, which names neither the property nor its value and so gave no clue which attribute of the resource was misconfigured. Such a value now leads to the NamingException which the contract promises, naming the value, the property and the type, with the exception of the conversion as its cause. A value which can be converted is converted as before. Add tests which check both a value which is not a number for a property which is an integer and an empty value for a character property. --- java/org/apache/naming/factory/BeanFactory.java | 70 +++++++++++++--------- .../apache/naming/factory/LocalStrings.properties | 1 + .../org/apache/naming/factory/TestBeanFactory.java | 51 ++++++++++++++++ test/org/apache/naming/factory/TesterBean.java | 20 +++++++ 4 files changed, 114 insertions(+), 28 deletions(-) diff --git a/java/org/apache/naming/factory/BeanFactory.java b/java/org/apache/naming/factory/BeanFactory.java index 0d5a785cec..c9d9badb7f 100644 --- a/java/org/apache/naming/factory/BeanFactory.java +++ b/java/org/apache/naming/factory/BeanFactory.java @@ -139,38 +139,52 @@ public class BeanFactory implements ObjectFactory { Class<?> propType = pda[i].getPropertyType(); Method setProp = pda[i].getWriteMethod(); - if (propType.equals(String.class)) { - valueArray[0] = value; - } else if (propType.equals(Character.class) || propType.equals(char.class)) { - valueArray[0] = Character.valueOf(value.charAt(0)); - } else if (propType.equals(Byte.class) || propType.equals(byte.class)) { - valueArray[0] = Byte.valueOf(value); - } else if (propType.equals(Short.class) || propType.equals(short.class)) { - valueArray[0] = Short.valueOf(value); - } else if (propType.equals(Integer.class) || propType.equals(int.class)) { - valueArray[0] = Integer.valueOf(value); - } else if (propType.equals(Long.class) || propType.equals(long.class)) { - valueArray[0] = Long.valueOf(value); - } else if (propType.equals(Float.class) || propType.equals(float.class)) { - valueArray[0] = Float.valueOf(value); - } else if (propType.equals(Double.class) || propType.equals(double.class)) { - valueArray[0] = Double.valueOf(value); - } else if (propType.equals(Boolean.class) || propType.equals(boolean.class)) { - valueArray[0] = Boolean.valueOf(value); - } else if (setProp != null) { - // This is a Tomcat specific extension and is not part of the - // Java Bean specification. - String setterName = setProp.getName(); - try { - setProp = bean.getClass().getMethod(setterName, String.class); + // The conversion methods of the numeric types and + // the reading of the first character report an + // invalid value of a configured property with an + // unchecked exception, which is replaced here by a + // NamingException which names the property, its + // value and its type. + try { + if (propType.equals(String.class)) { valueArray[0] = value; - } catch (NoSuchMethodException nsme) { + } else if (propType.equals(Character.class) || propType.equals(char.class)) { + valueArray[0] = Character.valueOf(value.charAt(0)); + } else if (propType.equals(Byte.class) || propType.equals(byte.class)) { + valueArray[0] = Byte.valueOf(value); + } else if (propType.equals(Short.class) || propType.equals(short.class)) { + valueArray[0] = Short.valueOf(value); + } else if (propType.equals(Integer.class) || propType.equals(int.class)) { + valueArray[0] = Integer.valueOf(value); + } else if (propType.equals(Long.class) || propType.equals(long.class)) { + valueArray[0] = Long.valueOf(value); + } else if (propType.equals(Float.class) || propType.equals(float.class)) { + valueArray[0] = Float.valueOf(value); + } else if (propType.equals(Double.class) || propType.equals(double.class)) { + valueArray[0] = Double.valueOf(value); + } else if (propType.equals(Boolean.class) || propType.equals(boolean.class)) { + valueArray[0] = Boolean.valueOf(value); + } else if (setProp != null) { + // This is a Tomcat specific extension and is not part of the + // Java Bean specification. + String setterName = setProp.getName(); + try { + setProp = bean.getClass().getMethod(setterName, String.class); + valueArray[0] = value; + } catch (NoSuchMethodException nsme) { + throw new NamingException( + sm.getString("beanFactory.noStringConversion", propName, + propType.getName())); + } + } else { throw new NamingException(sm.getString("beanFactory.noStringConversion", propName, propType.getName())); } - } else { - throw new NamingException( - sm.getString("beanFactory.noStringConversion", propName, propType.getName())); + } catch (NumberFormatException | StringIndexOutOfBoundsException ex) { + NamingException ne = new NamingException(sm.getString("beanFactory.invalidValue", + value, propName, propType.getName())); + ne.initCause(ex); + throw ne; } if (setProp != null) { diff --git a/java/org/apache/naming/factory/LocalStrings.properties b/java/org/apache/naming/factory/LocalStrings.properties index 4a54e94849..04843fa857 100644 --- a/java/org/apache/naming/factory/LocalStrings.properties +++ b/java/org/apache/naming/factory/LocalStrings.properties @@ -17,6 +17,7 @@ # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations beanFactory.classNotFound=Class not found: [{0}] +beanFactory.invalidValue=The value [{0}] of the property [{1}] cannot be converted to the type [{2}] beanFactory.noForceString=The forceString option has been removed as a security hardening measure. Instead, if the setter method doesn't use String, a primitive or a primitive wrapper, the factory will look for a method with the same name as the setter that accepts a String and use that if found. beanFactory.noSetMethod=No set method found for property [{0}] beanFactory.noStringConversion=String conversion for property [{0}] of type [{1}] not available diff --git a/test/org/apache/naming/factory/TestBeanFactory.java b/test/org/apache/naming/factory/TestBeanFactory.java index 79912c7d0c..ceb2211a7b 100644 --- a/test/org/apache/naming/factory/TestBeanFactory.java +++ b/test/org/apache/naming/factory/TestBeanFactory.java @@ -16,6 +16,7 @@ */ package org.apache.naming.factory; +import javax.naming.NamingException; import javax.naming.StringRefAddr; import org.junit.Assert; @@ -91,4 +92,54 @@ public class TestBeanFactory { Assert.assertNotNull(result.getServer()); Assert.assertEquals(IP_ADDRESS, result.getServer().getHostAddress()); } + + + /* + * A value which does not match the type of the property is a configuration + * error which has to be reported as a NamingException with the property as + * context, not as the unchecked exception of the conversion. + */ + @Test + public void testValueNotMatchingThePropertyTypeIsReported() throws Exception { + + ResourceRef resourceRef = new ResourceRef(TesterBean.class.getName(), null, null, null, false); + resourceRef.add(new StringRefAddr("limit", "not-a-number")); + + BeanFactory factory = new BeanFactory(); + + try { + Object obj = factory.getObjectInstance(resourceRef, null, null, null); + Assert.fail("The invalid value was accepted: " + obj); + } catch (NamingException e) { + Assert.assertTrue("The property is not named: " + e.getMessage(), e.getMessage().contains("limit")); + Assert.assertTrue("The value is not named: " + e.getMessage(), e.getMessage().contains("not-a-number")); + Assert.assertTrue("The reason for the failure is not reported", + e.getCause() instanceof NumberFormatException); + } + } + + + /* + * The value of a character property is read from the first character of + * the configured value, so an empty value has to be reported in the same + * way as a value which cannot be converted. + */ + @Test + public void testEmptyValueForCharacterPropertyIsReported() throws Exception { + + ResourceRef resourceRef = new ResourceRef(TesterBean.class.getName(), null, null, null, false); + resourceRef.add(new StringRefAddr("letter", "")); + + BeanFactory factory = new BeanFactory(); + + try { + Object obj = factory.getObjectInstance(resourceRef, null, null, null); + Assert.fail("The invalid value was accepted: " + obj); + } catch (NamingException e) { + Assert.assertTrue("The property is not named: " + e.getMessage(), e.getMessage().contains("letter")); + Assert.assertTrue("The type is not named: " + e.getMessage(), e.getMessage().contains("char")); + Assert.assertTrue("The reason for the failure is not reported", + e.getCause() instanceof StringIndexOutOfBoundsException); + } + } } diff --git a/test/org/apache/naming/factory/TesterBean.java b/test/org/apache/naming/factory/TesterBean.java index d2beef275c..654e9fef28 100644 --- a/test/org/apache/naming/factory/TesterBean.java +++ b/test/org/apache/naming/factory/TesterBean.java @@ -23,6 +23,10 @@ public class TesterBean { private InetAddress server; + private int limit; + + private char letter; + public InetAddress getServer() { return server; } @@ -38,4 +42,20 @@ public class TesterBean { throw new IllegalArgumentException(e); } } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public char getLetter() { + return letter; + } + + public void setLetter(char letter) { + this.letter = letter; + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
