diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 17f5ae4d2617..4cd59c3a8795 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -515,10 +515,18 @@ public void configure(Object obj, XmlParser.Node cfg, int i) throws Exception */ private void set(Object obj, XmlParser.Node node) throws Exception { - String attr = node.getAttribute("name"); + String name = node.getAttribute("name"); + String setter = "set" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1); String id = node.getAttribute("id"); String property = node.getAttribute("property"); String propertyValue = null; + + Class oClass = nodeClass(node); + if (oClass != null) + obj = null; + else + oClass = obj.getClass(); + // Look for a property value if (property != null) { @@ -526,27 +534,31 @@ private void set(Object obj, XmlParser.Node node) throws Exception propertyValue = properties.get(property); // If no property value, then do not set if (propertyValue == null) + { + // check that there is at least one setter or field that could have matched + if (Arrays.stream(oClass.getMethods()).noneMatch(m -> m.getName().equals(setter)) && + Arrays.stream(oClass.getFields()).filter(f -> Modifier.isPublic(f.getModifiers())).noneMatch(f -> f.getName().equals(name))) + { + NoSuchMethodException e = new NoSuchMethodException(String.format("No method '%s' on %s", setter, oClass.getName())); + e.addSuppressed(new NoSuchFieldException(String.format("No field '%s' on %s", name, oClass.getName()))); + throw e; + } + // otherwise it is a noop return; + } } - String name = "set" + attr.substring(0, 1).toUpperCase(Locale.ENGLISH) + attr.substring(1); Object value = value(obj, node); if (value == null) value = propertyValue; Object[] arg = {value}; - Class oClass = nodeClass(node); - if (oClass != null) - obj = null; - else - oClass = obj.getClass(); - Class[] vClass = {Object.class}; if (value != null) vClass[0] = value.getClass(); if (LOG.isDebugEnabled()) - LOG.debug("XML {}.{} ({})", (obj != null ? obj.toString() : oClass.getName()), name, value); + LOG.debug("XML {}.{} ({})", (obj != null ? obj.toString() : oClass.getName()), setter, value); MultiException me = new MultiException(); @@ -557,7 +569,7 @@ private void set(Object obj, XmlParser.Node node) throws Exception // Try for trivial match try { - Method set = oClass.getMethod(name, vClass); + Method set = oClass.getMethod(setter, vClass); invokeMethod(set, obj, arg); return; } @@ -572,7 +584,7 @@ private void set(Object obj, XmlParser.Node node) throws Exception { Field type = vClass[0].getField("TYPE"); vClass[0] = (Class)type.get(null); - Method set = oClass.getMethod(name, vClass); + Method set = oClass.getMethod(setter, vClass); invokeMethod(set, obj, arg); return; } @@ -585,7 +597,7 @@ private void set(Object obj, XmlParser.Node node) throws Exception // Try a field try { - Field field = oClass.getField(attr); + Field field = oClass.getField(name); if (Modifier.isPublic(field.getModifiers())) { try @@ -622,18 +634,18 @@ private void set(Object obj, XmlParser.Node node) throws Exception // Search for a match by trying all the set methods Method[] sets = oClass.getMethods(); Method set = null; - for (Method setter : sets) + for (Method s : sets) { - if (setter.getParameterCount() != 1) + if (s.getParameterCount() != 1) continue; - Class[] paramTypes = setter.getParameterTypes(); - if (name.equals(setter.getName())) + Class[] paramTypes = s.getParameterTypes(); + if (setter.equals(s.getName())) { types = types == null ? paramTypes[0].getName() : (types + "," + paramTypes[0].getName()); // lets try it try { - set = setter; + set = s; invokeMethod(set, obj, arg); return; } @@ -650,7 +662,7 @@ private void set(Object obj, XmlParser.Node node) throws Exception if (paramTypes[0].isAssignableFrom(c)) { setValue = convertArrayToCollection(value, c); - invokeMethod(setter, obj, setValue); + invokeMethod(s, obj, setValue); return; } } @@ -703,7 +715,7 @@ private void set(Object obj, XmlParser.Node node) throws Exception } // No Joy - String message = oClass + "." + name + "(" + vClass[0] + ")"; + String message = oClass + "." + setter + "(" + vClass[0] + ")"; if (types != null) message += ". Found setters for " + types; NoSuchMethodException failure = new NoSuchMethodException(message); diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java index 81d00277499c..5ffd5190c88d 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java @@ -327,6 +327,30 @@ public void testSetWithProperty() throws Exception assertEquals(configuration.getIdMap().get("test"), "This is a property value"); } + @Test + public void testSetFieldWithProperty() throws Exception + { + XmlConfiguration configuration = asXmlConfiguration(""); + configuration.getProperties().put("prop", "42"); + TestConfiguration tc = new TestConfiguration(); + tc.testField1 = -1; + configuration.configure(tc); + assertEquals(42, tc.testField1); + assertEquals(configuration.getIdMap().get("test"), "42"); + } + + @Test + public void testNotSetFieldWithNullProperty() throws Exception + { + XmlConfiguration configuration = asXmlConfiguration(""); + configuration.getProperties().remove("prop"); + TestConfiguration tc = new TestConfiguration(); + tc.testField1 = -1; + configuration.configure(tc); + assertEquals(-1, tc.testField1); + assertEquals(configuration.getIdMap().get("test"), null); + } + @Test public void testSetWithNullProperty() throws Exception { @@ -363,6 +387,33 @@ public void testSetWithNullPropertyAndValue() throws Exception assertNull(configuration.getIdMap().get("test")); } + @Test + public void testSetWithWrongNameAndProperty() throws Exception + { + XmlConfiguration configuration = asXmlConfiguration(""); + configuration.getProperties().put("prop", "This is a property value"); + TestConfiguration tc = new TestConfiguration(); + tc.setTestString("default"); + + NoSuchMethodException e = assertThrows(NoSuchMethodException.class, () -> configuration.configure(tc)); + assertThat(e.getMessage(), containsString("setWrongName")); + assertEquals("default", tc.getTestString()); + } + + @Test + public void testSetWithWrongNameAndNullProperty() throws Exception + { + XmlConfiguration configuration = asXmlConfiguration(""); + configuration.getProperties().remove("prop"); + TestConfiguration tc = new TestConfiguration(); + tc.setTestString("default"); + + NoSuchMethodException e = assertThrows(NoSuchMethodException.class, () -> configuration.configure(tc)); + assertThat(e.getMessage(), containsString("setWrongName")); + assertThat(e.getSuppressed()[0], instanceOf(NoSuchFieldException.class)); + assertEquals("default", tc.getTestString()); + } + @Test public void testMeaningfullSetException() throws Exception {