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 22326419de2..347a21d6319 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 @@ -23,6 +23,7 @@ import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -39,9 +40,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -51,7 +52,6 @@ import java.util.Queue; import java.util.ServiceLoader; import java.util.Set; -import org.eclipse.jetty.util.ArrayUtil; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.MultiException; @@ -82,21 +82,37 @@ import org.xml.sax.SAXException; public class XmlConfiguration { private static final Logger LOG = Log.getLogger(XmlConfiguration.class); - private static final Class[] __primitives = + private static final Class[] PRIMITIVES = + { + Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE + }; + private static final Class[] BOXED_PRIMITIVES = + { + Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, + Void.class + }; + private static final Class[] SUPPORTED_COLLECTIONS = + { + ArrayList.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class + }; + private static final Iterable PROCESSOR_FACTORIES = ServiceLoader.load(ConfigurationProcessorFactory.class); + private static final XmlParser PARSER = initParser(); + private static final Comparator EXECUTABLE_COMPARATOR = (o1, o2) -> { - Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE + int p1 = o1.getParameterCount(); + int p2 = o2.getParameterCount(); + int compare = Integer.compare(p1, p2); + if (compare == 0 && p1 > 0) + { + boolean a1 = o1.getParameterTypes()[p1 - 1].isArray(); + boolean a2 = o2.getParameterTypes()[p2 - 1].isArray(); + if (a1 && !a2) + compare = 1; + else if (!a1 && a2) + compare = -1; + } + return compare; }; - private static final Class[] __boxedPrimitives = - { - Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, - Void.class - }; - private static final Class[] __supportedCollections = - { - ArrayList.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class - }; - private static final Iterable __factoryLoader = ServiceLoader.load(ConfigurationProcessorFactory.class); - private static final XmlParser __parser = initParser(); private static XmlParser initParser() { @@ -201,14 +217,14 @@ public class XmlConfiguration */ public XmlConfiguration(Resource resource) throws SAXException, IOException { - synchronized (__parser) + synchronized (PARSER) { _location = resource; try (InputStream inputStream = resource.getInputStream()) { - setConfig(__parser.parse(inputStream)); + setConfig(PARSER.parse(inputStream)); } - _dtd = __parser.getDTD(); + _dtd = PARSER.getDTD(); } } @@ -228,9 +244,9 @@ public class XmlConfiguration { _processor = new JettyXmlConfiguration(); } - else if (__factoryLoader != null) + else if (PROCESSOR_FACTORIES != null) { - for (ConfigurationProcessorFactory factory : __factoryLoader) + for (ConfigurationProcessorFactory factory : PROCESSOR_FACTORIES) { _processor = factory.getConfigurationProcessor(_dtd, config.getTag()); if (_processor != null) @@ -356,39 +372,13 @@ public class XmlConfiguration int index = 0; if (obj == null && oClass != null) { - index = _root.size(); - Map namedArgMap = new HashMap<>(); - - List arguments = new LinkedList<>(); - for (int i = 0; i < _root.size(); i++) - { - Object o = _root.get(i); - if (o instanceof String) - continue; - - XmlParser.Node node = (XmlParser.Node)o; - if (node.getTag().equals("Arg")) - { - String namedAttribute = node.getAttribute("name"); - Object value = value(null, (XmlParser.Node)o); - if (namedAttribute != null) - namedArgMap.put(namedAttribute, value); - arguments.add(value); - } - else - { - index = i; - break; - } - } - try { - obj = construct(oClass, arguments.toArray(), namedArgMap); + obj = construct(oClass, new Args(null, oClass, XmlConfiguration.getNodes(_root, "Arg"))); } catch (NoSuchMethodException x) { - throw new IllegalStateException(String.format("No constructor %s(%s,%s) in %s", oClass, arguments, namedArgMap, _configuration)); + throw new IllegalStateException(String.format("No matching constructor %s in %s", oClass, _configuration)); } } if (id != null) @@ -399,6 +389,14 @@ public class XmlConfiguration return obj; } + private static Class nodeClass(XmlParser.Node node) throws ClassNotFoundException + { + String className = node.getAttribute("class"); + if (className == null) + return null; + return Loader.loadClass(className); + } + /** * Recursive configuration routine. * This method applies the nested Set, Put, Call, etc. elements to the given object. @@ -483,14 +481,6 @@ public class XmlConfiguration } } - private static Class nodeClass(XmlParser.Node node) throws ClassNotFoundException - { - String className = node.getAttribute("class"); - if (className == null) - return null; - return Loader.loadClass(className); - } - /** *

Call a setter method.

*

This method makes a best effort to find a matching set method. @@ -637,7 +627,7 @@ public class XmlConfiguration try { - for (Class c : __supportedCollections) + for (Class c : SUPPORTED_COLLECTIONS) { if (paramTypes[0].isAssignableFrom(c)) { @@ -663,11 +653,11 @@ public class XmlConfiguration Class sClass = set.getParameterTypes()[0]; if (sClass.isPrimitive()) { - for (int t = 0; t < __primitives.length; t++) + for (int t = 0; t < PRIMITIVES.length; t++) { - if (sClass.equals(__primitives[t])) + if (sClass.equals(PRIMITIVES[t])) { - sClass = __boxedPrimitives[t]; + sClass = BOXED_PRIMITIVES[t]; break; } } @@ -863,7 +853,6 @@ public class XmlConfiguration String id = aoeNode.getString("Id"); String name = aoeNode.getString("Name"); String clazz = aoeNode.getString("Class"); - List args = aoeNode.getList("Arg"); Class oClass; if (clazz != null) @@ -884,7 +873,7 @@ public class XmlConfiguration try { - Object nobj = call(oClass, name, obj, args.toArray(new Object[0])); + Object nobj = call(oClass, name, obj, new Args(obj, oClass, aoeNode.getNodes("Arg"))); if (id != null) _configuration.getIdMap().put(id, nobj); configure(nobj, node, aoeNode.getNext()); @@ -896,7 +885,7 @@ public class XmlConfiguration } } - private Object call(Class oClass, String methodName, Object obj, Object[] arg) throws InvocationTargetException, NoSuchMethodException + private Object call(Class oClass, String methodName, Object obj, Args args) throws InvocationTargetException, NoSuchMethodException { Objects.requireNonNull(oClass, "Class cannot be null"); Objects.requireNonNull(methodName, "Method name cannot be null"); @@ -904,11 +893,15 @@ public class XmlConfiguration throw new IllegalArgumentException("Method name cannot be blank"); // Lets just try all methods for now - for (Method method : oClass.getMethods()) + + Method[] methods = oClass.getMethods(); + Arrays.sort(methods, EXECUTABLE_COMPARATOR); + for (Method method : methods) { if (!method.getName().equals(methodName)) continue; - if (method.getParameterCount() != arg.length) + Object[] arguments = args.applyTo(method); + if (arguments == null) continue; if (Modifier.isStatic(method.getModifiers()) != (obj == null)) continue; @@ -917,34 +910,7 @@ public class XmlConfiguration try { - return invokeMethod(method, obj, arg); - } - catch (IllegalAccessException | IllegalArgumentException e) - { - LOG.ignore(e); - } - } - - // Lets look for a method with varargs arguments - Object[] argsWithVarargs = null; - for (Method method : oClass.getMethods()) - { - if (!method.getName().equals(methodName)) - continue; - if (method.getParameterCount() != arg.length + 1) - continue; - if (!method.getParameterTypes()[arg.length].isArray()) - continue; - if (Modifier.isStatic(method.getModifiers()) != (obj == null)) - continue; - if ((obj == null) && method.getDeclaringClass() != oClass) - continue; - - if (argsWithVarargs == null) - argsWithVarargs = ArrayUtil.addToArray(arg, new Object[0], Object.class); - try - { - return invokeMethod(method, obj, argsWithVarargs); + return invokeMethod(method, obj, arguments); } catch (IllegalAccessException | IllegalArgumentException e) { @@ -964,36 +930,19 @@ public class XmlConfiguration */ private Object newObj(Object obj, XmlParser.Node node) throws Exception { - final AttrOrElementNode aoeNode = new AttrOrElementNode(obj, node, "Id", "Class", "Arg"); - final String id = aoeNode.getString("Id"); - final String clazz = aoeNode.getString("Class"); - final List argNodes = aoeNode.getNodes("Arg"); + AttrOrElementNode aoeNode = new AttrOrElementNode(obj, node, "Id", "Class", "Arg"); + String id = aoeNode.getString("Id"); + String clazz = aoeNode.getString("Class"); if (LOG.isDebugEnabled()) LOG.debug("XML new " + clazz); Class oClass = Loader.loadClass(clazz); - // Find the elements - Map namedArgMap = new HashMap<>(); - List arguments = new LinkedList<>(); - for (XmlParser.Node child : argNodes) - { - String namedAttribute = child.getAttribute("name"); - Object value = value(obj, child); - if (namedAttribute != null) - { - // named arguments - namedArgMap.put(namedAttribute, value); - } - // raw arguments - arguments.add(value); - } - Object nobj; try { - nobj = construct(oClass, arguments.toArray(), namedArgMap); + nobj = construct(oClass, new Args(obj, oClass, aoeNode.getNodes("Arg"))); } catch (NoSuchMethodException e) { @@ -1008,80 +957,20 @@ public class XmlConfiguration return nobj; } - private Object construct(Class klass, Object[] arguments, Map namedArgMap) throws InvocationTargetException, NoSuchMethodException + private Object construct(Class klass, Args args) throws InvocationTargetException, NoSuchMethodException { Objects.requireNonNull(klass, "Class cannot be null"); - Objects.requireNonNull(namedArgMap, "Named Argument Map cannot be null"); + Objects.requireNonNull(args, "Named list cannot be null"); - for (Constructor constructor : klass.getConstructors()) + Constructor[] constructors = klass.getConstructors(); + Arrays.sort(constructors, EXECUTABLE_COMPARATOR); + for (Constructor constructor : constructors) { - if (arguments == null) - { - // null arguments in .newInstance() is allowed - if (constructor.getParameterCount() != 0) - continue; - } - else if (constructor.getParameterCount() != arguments.length) - { - continue; - } - try { - if (arguments == null || arguments.length == 0) - { - if (LOG.isDebugEnabled()) - LOG.debug("Invoking constructor, no arguments"); - return invokeConstructor(constructor); - } - - if (namedArgMap.isEmpty()) - { - if (LOG.isDebugEnabled()) - LOG.debug("Invoking constructor, no XML parameter mapping"); + Object[] arguments = args.applyTo(constructor); + if (arguments != null) return invokeConstructor(constructor, arguments); - } - - Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); - if (parameterAnnotations == null || parameterAnnotations.length == 0) - { - if (LOG.isDebugEnabled()) - LOG.debug("Invoking constructor, no parameter annotations"); - return invokeConstructor(constructor, arguments); - } - - int count = 0; - Object[] swizzled = new Object[arguments.length]; - for (Annotation[] annotations : parameterAnnotations) - { - for (Annotation annotation : annotations) - { - if (annotation instanceof Name) - { - Name param = (Name)annotation; - if (namedArgMap.containsKey(param.value())) - { - if (LOG.isDebugEnabled()) - LOG.debug("Mapping named parameter {} in position {}", param.value(), count); - swizzled[count] = namedArgMap.get(param.value()); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Mapping argument {} in position {}", arguments[count], count); - swizzled[count] = arguments[count]; - } - ++count; - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Skipping parameter annotated with {}", annotation); - } - } - } - - return invokeConstructor(constructor, swizzled); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) { @@ -1228,11 +1117,11 @@ public class XmlConfiguration */ private Object propertyObj(XmlParser.Node node) throws Exception { - final AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Name", "Deprecated", "Default"); - final String id = aoeNode.getString("Id"); - final String name = aoeNode.getString("Name", true); - final List deprecated = aoeNode.getList("Deprecated"); - final String dftValue = aoeNode.getString("Default"); + AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Name", "Deprecated", "Default"); + String id = aoeNode.getString("Id"); + String name = aoeNode.getString("Name", true); + List deprecated = aoeNode.getList("Deprecated"); + String dftValue = aoeNode.getString("Default"); // Look for a value Map properties = _configuration.getProperties(); @@ -1279,11 +1168,11 @@ public class XmlConfiguration */ private Object systemPropertyObj(XmlParser.Node node) throws Exception { - final AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Name", "Deprecated", "Default"); - final String id = aoeNode.getString("Id"); - final String name = aoeNode.getString("Name", true); - final List deprecated = aoeNode.getList("Deprecated"); - final String dftValue = aoeNode.getString("Default"); + AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Name", "Deprecated", "Default"); + String id = aoeNode.getString("Id"); + String name = aoeNode.getString("Name", true); + List deprecated = aoeNode.getList("Deprecated"); + String dftValue = aoeNode.getString("Default"); // Look for a value String value = System.getProperty(name); @@ -1509,7 +1398,7 @@ public class XmlConfiguration } } - for (Class collectionClass : __supportedCollections) + for (Class collectionClass : SUPPORTED_COLLECTIONS) { if (isTypeMatchingClass(type, collectionClass)) return convertArrayToCollection(value, collectionClass); @@ -1685,40 +1574,151 @@ public class XmlConfiguration public List getNodes(String elementName) { - String attrName = StringUtil.asciiToLowerCase(elementName); - final List values = new ArrayList<>(); - - String attr = _node.getAttribute(attrName); - if (attr != null) - { - for (String a : StringUtil.csvSplit(null, attr, 0, attr.length())) - { - // create a fake node - XmlParser.Node n = new XmlParser.Node(null, elementName, null); - n.add(a); - values.add(n); - } - } - - for (int i = 0; i < _next; i++) - { - Object o = _node.get(i); - if (!(o instanceof XmlParser.Node)) - continue; - XmlParser.Node n = (XmlParser.Node)o; - - if (elementName.equals(n.getTag())) - { - if (attr != null) - throw new IllegalStateException("Cannot have attr '" + attrName + "' and element '" + elementName + "'"); - - values.add(n); - } - } - - return values; + return XmlConfiguration.getNodes(_node, elementName); } } + + private class Args + { + private final Class _class; + private final List _arguments; + private final List _names; + + private Args(Object obj, Class oClass, List args) throws Exception + { + _class = oClass; + _arguments = new ArrayList<>(); + _names = new ArrayList<>(); + for (XmlParser.Node child : args) + { + _arguments.add(value(obj, child)); + _names.add(child.getAttribute("name")); + } + } + + private Args(List arguments, List names) + { + _class = null; + _arguments = arguments; + _names = names; + } + + Object[] applyTo(Executable executable) + { + Object[] args = matchArgsToParameters(executable); + if (args == null && _class != null) + { + // Could this be an empty varargs match? + int count = executable.getParameterCount(); + if (count > 0 && executable.getParameterTypes()[count - 1].isArray()) + { + // There is not a no varArgs alternative so let's try a an empty varArgs match + args = asEmptyVarArgs(executable.getParameterTypes()[count - 1]).matchArgsToParameters(executable); + } + } + return args; + } + + Args asEmptyVarArgs(Class varArgType) + { + List arguments = new ArrayList<>(_arguments); + arguments.add(Array.newInstance(varArgType.getComponentType(), 0)); + List names = new ArrayList<>(_names); + names.add(null); + return new Args(arguments, names); + } + + Object[] matchArgsToParameters(Executable executable) + { + int count = executable.getParameterCount(); + + // No match of wrong number of parameters + if (count != _arguments.size()) + return null; + + // Handle no parameter case + if (count == 0) + return new Object[0]; + + // If no arg names are specified, keep the arg order + if (_names.stream().noneMatch(Objects::nonNull)) + return _arguments.toArray(new Object[0]); + + // If we don't have any parameters with names, then no match + Annotation[][] parameterAnnotations = executable.getParameterAnnotations(); + if (parameterAnnotations == null || parameterAnnotations.length == 0) + return null; + + // Find the position of all named parameters from the executable + Map position = new HashMap<>(); + int p = 0; + for (Annotation[] paramAnnotation : parameterAnnotations) + { + Integer pos = p++; + Arrays.stream(paramAnnotation) + .filter(Name.class::isInstance) + .map(Name.class::cast) + .findFirst().ifPresent(n -> position.put(n.value(), pos)); + } + + List arguments = new ArrayList<>(_arguments); + List names = new ArrayList<>(_names); + // Map the actual arguments to the names + for (p = 0; p < count; p++) + { + String name = names.get(p); + if (name != null) + { + Integer pos = position.get(name); + if (pos == null) + return null; + if (pos != p) + { + // adjust position of parameter + arguments.add(pos, arguments.remove(p)); + names.add(pos, names.remove(p)); + p = Math.min(p, pos); + } + } + } + return arguments.toArray(new Object[0]); + } + } + } + + private static List getNodes(XmlParser.Node node, String elementName) + { + String attrName = StringUtil.asciiToLowerCase(elementName); + final List values = new ArrayList<>(); + + String attr = node.getAttribute(attrName); + if (attr != null) + { + for (String a : StringUtil.csvSplit(null, attr, 0, attr.length())) + { + // create a fake node + XmlParser.Node n = new XmlParser.Node(null, elementName, null); + n.add(a); + values.add(n); + } + } + + for (Object o : node) + { + if (!(o instanceof XmlParser.Node)) + continue; + XmlParser.Node n = (XmlParser.Node)o; + + if (elementName.equals(n.getTag())) + { + if (attr != null) + throw new IllegalStateException("Cannot have attr '" + attrName + "' and element '" + elementName + "'"); + + values.add(n); + } + } + + return values; } /** @@ -1789,7 +1789,7 @@ public class XmlConfiguration } if (LOG.isDebugEnabled()) - LOG.debug("objects={}", Arrays.asList(objects)); + LOG.debug("objects={}", objects); // For all objects created by XmlConfigurations, start them if they are lifecycles. List started = new ArrayList<>(objects.size()); diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java index ef7fcb8e60f..e38b41cddc8 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java @@ -37,6 +37,18 @@ public class AnnotatedTestConfiguration { } + public AnnotatedTestConfiguration(Integer test) + { + // exists to make constructor matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public AnnotatedTestConfiguration(Integer one, Integer two, Integer three) + { + // exists to make constructor matching harder + throw new UnsupportedOperationException("Should not be called"); + } + public AnnotatedTestConfiguration(@Name("first") String first, @Name("second") String second, @Name("third") String third) { this.first = first; @@ -44,6 +56,38 @@ public class AnnotatedTestConfiguration this.third = third; } + public AnnotatedTestConfiguration(Long one, Long two, Long three) + { + // exists to make constructor matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public void setAll(Integer one, Integer two, Integer three) + { + // exists to make method matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public void setAll(@Name("first") String first, @Name("second") String second, @Name("third") String third) + { + this.first = first; + this.second = second; + this.third = third; + } + + public void setAll(long one, long two, long three) + { + // exists to make method matching harder + throw new UnsupportedOperationException("Should not be called"); + } + + public void setVarArgs(String first, String... theRest) + { + this.first = first; + this.second = theRest.length > 0 ? theRest[0] : null; + this.third = theRest.length > 1 ? theRest[1] : null; + } + public String getFirst() { return first; 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 518878d25ae..9acdae8aa04 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 @@ -1,19 +1,19 @@ // -// ======================================================================== -// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. // -// This program and the accompanying materials are made available under -// the terms of the Eclipse Public License 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0 +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html // -// This Source Code may also be made available under the following -// Secondary Licenses when the conditions for such availability set -// forth in the Eclipse Public License, v. 2.0 are satisfied: -// the Apache License v2.0 which is available at -// https://www.apache.org/licenses/LICENSE-2.0 +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php // -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== // package org.eclipse.jetty.xml; @@ -44,6 +44,8 @@ import org.eclipse.jetty.util.resource.Resource; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.xml.sax.SAXException; import static java.nio.charset.StandardCharsets.UTF_8; @@ -66,10 +68,6 @@ public class XmlConfigurationTest { public WorkDir workDir; - protected String[] _configure = new String[]{ - "org/eclipse/jetty/xml/configureWithAttr.xml", "org/eclipse/jetty/xml/configureWithElements.xml" - }; - private static final String STRING_ARRAY_XML = "String1String2"; private static final String INT_ARRAY_XML = "12"; @@ -82,170 +80,175 @@ public class XmlConfigurationTest configuration.configure(); } - @Test - public void testPassedObject() throws Exception + public static String[] xmlConfigs() { - for (String configure : _configure) - { - Map properties = new HashMap<>(); - properties.put("whatever", "xxx"); - TestConfiguration.VALUE = 77; - URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); - XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(url)); - TestConfiguration tc = new TestConfiguration("tc"); - configuration.getProperties().putAll(properties); - configuration.configure(tc); - - assertEquals("SetValue", tc.testObject, "Set String"); - assertEquals(2, tc.testInt, "Set Type"); - - assertEquals(18080, tc.propValue); - - assertEquals("PutValue", tc.get("Test"), "Put"); - assertEquals("2", tc.get("TestDft"), "Put dft"); - assertEquals(2, tc.get("TestInt"), "Put type"); - - assertEquals("PutValue", tc.get("Trim"), "Trim"); - assertNull(tc.get("Null"), "Null"); - assertNull(tc.get("NullTrim"), "NullTrim"); - - assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); - assertEquals("-1String", tc.get("Objects"), "Objects"); - assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); - assertEquals("\n PutValue\n ", tc.get("String"), "String"); - assertEquals("", tc.get("NullString"), "NullString"); - assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); - assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); - assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); - assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); - - assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); - assertEquals(System.getenv("HOME"), tc.get("Env"), "Env"); - - assertEquals("xxx", tc.get("Property"), "Property"); - - assertEquals("Yes", tc.get("Called"), "Called"); - - assertTrue(TestConfiguration.called); - - assertEquals("Blah", tc.oa[0], "oa[0]"); - assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); - assertEquals(1.2345, tc.oa[2], "oa[2]"); - assertNull(tc.oa[3], "oa[3]"); - - assertEquals(1, tc.ia[0], "ia[0]"); - assertEquals(2, tc.ia[1], "ia[1]"); - assertEquals(3, tc.ia[2], "ia[2]"); - assertEquals(0, tc.ia[3], "ia[3]"); - - TestConfiguration tc2 = tc.nested; - assertNotNull(tc2); - assertEquals(true, tc2.get("Arg"), "Called(bool)"); - - assertNull(tc.get("Arg"), "nested config"); - assertEquals(true, tc2.get("Arg"), "nested config"); - - assertEquals("Call1", tc2.testObject, "nested config"); - assertEquals(4, tc2.testInt, "nested config"); - assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); - - assertEquals(tc.testField1, 77, "static to field"); - assertEquals(tc.testField2, 2, "field to field"); - assertEquals(TestConfiguration.VALUE, 42, "literal to static"); - - assertEquals(((Map)configuration.getIdMap().get("map")).get("key0"), "value0"); - assertEquals(((Map)configuration.getIdMap().get("map")).get("key1"), "value1"); - } + return new String[]{"org/eclipse/jetty/xml/configureWithAttr.xml", "org/eclipse/jetty/xml/configureWithElements.xml"}; } - @Test - public void testNewObject() throws Exception + @ParameterizedTest + @MethodSource("xmlConfigs") + public void testPassedObject(String configure) throws Exception { - for (String configure : _configure) + Map properties = new HashMap<>(); + properties.put("whatever", "xxx"); + TestConfiguration.VALUE = 77; + URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); + assertNotNull(url); + XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(url)); + TestConfiguration tc = new TestConfiguration("tc"); + configuration.getProperties().putAll(properties); + configuration.configure(tc); + + assertEquals("SetValue", tc.testObject, "Set String"); + assertEquals(2, tc.testInt, "Set Type"); + + assertEquals(18080, tc.propValue); + + assertEquals("PutValue", tc.get("Test"), "Put"); + assertEquals("2", tc.get("TestDft"), "Put dft"); + assertEquals(2, tc.get("TestInt"), "Put type"); + + assertEquals("PutValue", tc.get("Trim"), "Trim"); + assertNull(tc.get("Null"), "Null"); + assertNull(tc.get("NullTrim"), "NullTrim"); + + assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); + assertEquals("-1String", tc.get("Objects"), "Objects"); + assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); + assertEquals("\n PutValue\n ", tc.get("String"), "String"); + assertEquals("", tc.get("NullString"), "NullString"); + assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); + assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); + assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); + assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); + + assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); + assertEquals(System.getenv("HOME"), tc.get("Env"), "Env"); + + assertEquals("xxx", tc.get("Property"), "Property"); + + assertEquals("Yes", tc.get("Called"), "Called"); + + assertTrue(TestConfiguration.called); + + assertEquals("Blah", tc.oa[0], "oa[0]"); + assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); + assertEquals(1.2345, tc.oa[2], "oa[2]"); + assertNull(tc.oa[3], "oa[3]"); + + assertEquals(1, tc.ia[0], "ia[0]"); + assertEquals(2, tc.ia[1], "ia[1]"); + assertEquals(3, tc.ia[2], "ia[2]"); + assertEquals(0, tc.ia[3], "ia[3]"); + + TestConfiguration tc2 = tc.nested; + assertNotNull(tc2); + assertEquals(true, tc2.get("Arg"), "Called(bool)"); + + assertNull(tc.get("Arg"), "nested config"); + assertEquals(true, tc2.get("Arg"), "nested config"); + + assertEquals("Call1", tc2.testObject, "nested config"); + assertEquals(4, tc2.testInt, "nested config"); + assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); + + assertEquals(tc.testField1, 77, "static to field"); + assertEquals(tc.testField2, 2, "field to field"); + assertEquals(TestConfiguration.VALUE, 42, "literal to static"); + + @SuppressWarnings("unchecked") + Map map = (Map)configuration.getIdMap().get("map"); + assertEquals(map.get("key0"), "value0"); + assertEquals(map.get("key1"), "value1"); + } + + @ParameterizedTest + @MethodSource("xmlConfigs") + public void testNewObject(String configure) throws Exception + { + TestConfiguration.VALUE = 71; + Map properties = new HashMap<>(); + properties.put("whatever", "xxx"); + + URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); + assertNotNull(url); + AtomicInteger count = new AtomicInteger(0); + XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(url)) { - TestConfiguration.VALUE = 71; - Map properties = new HashMap<>(); - properties.put("whatever", "xxx"); - - URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure); - final AtomicInteger count = new AtomicInteger(0); - XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(url)) + @Override + public void initializeDefaults(Object object) { - @Override - public void initializeDefaults(Object object) + if (object instanceof TestConfiguration) { - if (object instanceof TestConfiguration) - { - count.incrementAndGet(); - ((TestConfiguration)object).setNested(null); - ((TestConfiguration)object).setTestString("NEW DEFAULT"); - } + count.incrementAndGet(); + ((TestConfiguration)object).setNested(null); + ((TestConfiguration)object).setTestString("NEW DEFAULT"); } - }; - configuration.getProperties().putAll(properties); - TestConfiguration tc = (TestConfiguration)configuration.configure(); + } + }; + configuration.getProperties().putAll(properties); + TestConfiguration tc = (TestConfiguration)configuration.configure(); - assertEquals(3, count.get()); + assertEquals(3, count.get()); - assertEquals("NEW DEFAULT", tc.getTestString()); - assertEquals("nested", tc.getNested().getTestString()); - assertEquals("NEW DEFAULT", tc.getNested().getNested().getTestString()); + assertEquals("NEW DEFAULT", tc.getTestString()); + assertEquals("nested", tc.getNested().getTestString()); + assertEquals("NEW DEFAULT", tc.getNested().getNested().getTestString()); - assertEquals("SetValue", tc.testObject, "Set String"); - assertEquals(2, tc.testInt, "Set Type"); + assertEquals("SetValue", tc.testObject, "Set String"); + assertEquals(2, tc.testInt, "Set Type"); - assertEquals(18080, tc.propValue); + assertEquals(18080, tc.propValue); - assertEquals("PutValue", tc.get("Test"), "Put"); - assertEquals("2", tc.get("TestDft"), "Put dft"); - assertEquals(2, tc.get("TestInt"), "Put type"); + assertEquals("PutValue", tc.get("Test"), "Put"); + assertEquals("2", tc.get("TestDft"), "Put dft"); + assertEquals(2, tc.get("TestInt"), "Put type"); - assertEquals("PutValue", tc.get("Trim"), "Trim"); - assertNull(tc.get("Null"), "Null"); - assertNull(tc.get("NullTrim"), "NullTrim"); + assertEquals("PutValue", tc.get("Trim"), "Trim"); + assertNull(tc.get("Null"), "Null"); + assertNull(tc.get("NullTrim"), "NullTrim"); - assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); - assertEquals("-1String", tc.get("Objects"), "Objects"); - assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); - assertEquals("\n PutValue\n ", tc.get("String"), "String"); - assertEquals("", tc.get("NullString"), "NullString"); - assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); - assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); - assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); - assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); + assertEquals(1.2345, tc.get("ObjectTrim"), "ObjectTrim"); + assertEquals("-1String", tc.get("Objects"), "Objects"); + assertEquals("-1String", tc.get("ObjectsTrim"), "ObjectsTrim"); + assertEquals("\n PutValue\n ", tc.get("String"), "String"); + assertEquals("", tc.get("NullString"), "NullString"); + assertEquals("\n ", tc.get("WhiteSpace"), "WhiteSpace"); + assertEquals("\n 1.2345\n ", tc.get("ObjectString"), "ObjectString"); + assertEquals("-1String", tc.get("ObjectsString"), "ObjectsString"); + assertEquals("-1\n String", tc.get("ObjectsWhiteString"), "ObjectsWhiteString"); - assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); - assertEquals("xxx", tc.get("Property"), "Property"); + assertEquals(System.getProperty("user.dir") + "/stuff", tc.get("SystemProperty"), "SystemProperty"); + assertEquals("xxx", tc.get("Property"), "Property"); - assertEquals("Yes", tc.get("Called"), "Called"); + assertEquals("Yes", tc.get("Called"), "Called"); - assertTrue(TestConfiguration.called); + assertTrue(TestConfiguration.called); - assertEquals("Blah", tc.oa[0], "oa[0]"); - assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); - assertEquals(1.2345, tc.oa[2], "oa[2]"); - assertNull(tc.oa[3], "oa[3]"); + assertEquals("Blah", tc.oa[0], "oa[0]"); + assertEquals("1.2.3.4:5678", tc.oa[1], "oa[1]"); + assertEquals(1.2345, tc.oa[2], "oa[2]"); + assertNull(tc.oa[3], "oa[3]"); - assertEquals(1, tc.ia[0], "ia[0]"); - assertEquals(2, tc.ia[1], "ia[1]"); - assertEquals(3, tc.ia[2], "ia[2]"); - assertEquals(0, tc.ia[3], "ia[3]"); + assertEquals(1, tc.ia[0], "ia[0]"); + assertEquals(2, tc.ia[1], "ia[1]"); + assertEquals(3, tc.ia[2], "ia[2]"); + assertEquals(0, tc.ia[3], "ia[3]"); - TestConfiguration tc2 = tc.nested; - assertNotNull(tc2); - assertEquals(true, tc2.get("Arg"), "Called(bool)"); + TestConfiguration tc2 = tc.nested; + assertNotNull(tc2); + assertEquals(true, tc2.get("Arg"), "Called(bool)"); - assertNull(tc.get("Arg"), "nested config"); - assertEquals(true, tc2.get("Arg"), "nested config"); + assertNull(tc.get("Arg"), "nested config"); + assertEquals(true, tc2.get("Arg"), "nested config"); - assertEquals("Call1", tc2.testObject, "nested config"); - assertEquals(4, tc2.testInt, "nested config"); - assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); + assertEquals("Call1", tc2.testObject, "nested config"); + assertEquals(4, tc2.testInt, "nested config"); + assertEquals("http://www.eclipse.com/", tc2.url.toString(), "nested call"); - assertEquals(71, tc.testField1, "static to field"); - assertEquals(2, tc.testField2, "field to field"); - assertEquals(42, TestConfiguration.VALUE, "literal to static"); - } + assertEquals(71, tc.testField1, "static to field"); + assertEquals(2, tc.testField2, "field to field"); + assertEquals(42, TestConfiguration.VALUE, "literal to static"); } public XmlConfiguration asXmlConfiguration(String rawXml) throws IOException, SAXException @@ -304,7 +307,7 @@ public class XmlConfigurationTest tc.setTestString("default"); configuration.configure(tc); assertEquals("default", tc.getTestString()); - assertEquals(configuration.getIdMap().get("test"), null); + assertNull(configuration.getIdMap().get("test")); } @Test @@ -328,7 +331,7 @@ public class XmlConfigurationTest tc.setTestString("default"); configuration.configure(tc); assertEquals("default", tc.getTestString()); - assertEquals(configuration.getIdMap().get("test"), null); + assertNull(configuration.getIdMap().get("test")); } @Test @@ -337,10 +340,7 @@ public class XmlConfigurationTest XmlConfiguration configuration = asXmlConfiguration(""); TestConfiguration tc = new TestConfiguration(); - NoSuchMethodException e = assertThrows(NoSuchMethodException.class, () -> - { - configuration.configure(tc); - }); + NoSuchMethodException e = assertThrows(NoSuchMethodException.class, () -> configuration.configure(tc)); assertThat(e.getMessage(), containsString("Found setters for int")); } @@ -380,10 +380,7 @@ public class XmlConfigurationTest "Some String"); TestConfiguration tc = new TestConfiguration(); - assertThrows(IllegalArgumentException.class, () -> - { - xmlConfiguration.configure(tc); - }); + assertThrows(IllegalArgumentException.class, () -> xmlConfiguration.configure(tc)); } @Test @@ -405,10 +402,7 @@ public class XmlConfigurationTest XmlConfiguration xmlConfiguration = asXmlConfiguration("" + "Some String"); TestConfiguration tc = new TestConfiguration(); - assertThrows(IllegalArgumentException.class, () -> - { - xmlConfiguration.configure(tc); - }); + assertThrows(IllegalArgumentException.class, () -> xmlConfiguration.configure(tc)); } @Test @@ -440,10 +434,7 @@ public class XmlConfigurationTest INT_ARRAY_XML + ""); TestConfiguration tc = new TestConfiguration(); assertThat("tc.getSet() returns null as it's not configured yet", tc.getList(), is(nullValue())); - assertThrows(NoSuchMethodException.class, () -> - { - xmlConfiguration.configure(tc); - }); + assertThrows(NoSuchMethodException.class, () -> xmlConfiguration.configure(tc)); } @Test @@ -696,6 +687,139 @@ public class XmlConfigurationTest assertEquals("arg3", atc.getNested().getThird(), "nested third parameter not wired correctly"); } + @Test + public void testCallNamedInjection() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionOrdered() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionUnOrdered() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg3 " + + " arg2 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionOrderedMixed() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallNamedInjectionUnorderedMixed() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " arg3 " + + " arg2 " + + " arg1 " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("arg1", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("arg2", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("arg3", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallVarArgs() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " one " + + " twothree " + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("one", atc.getFirst(), "first parameter not wired correctly"); + assertEquals("two", atc.getSecond(), "second parameter not wired correctly"); + assertEquals("three", atc.getThird(), "third parameter not wired correctly"); + } + + @Test + public void testCallMissingVarArgs() throws Exception + { + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " arg1 " + + " arg2 " + + " arg3 " + + " " + + " one" + + " " + + ""); + + AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure(); + + assertEquals("one", atc.getFirst(), "first parameter not wired correctly"); + assertNull(atc.getSecond()); + assertNull(atc.getThird()); + } + @Test public void testArgumentsGetIgnoredMissingDTD() throws Exception { @@ -859,7 +983,7 @@ public class XmlConfigurationTest " bad" + ""); - assertThrows(InvocationTargetException.class, () -> xmlConfiguration.configure()); + assertThrows(InvocationTargetException.class, xmlConfiguration::configure); } @Test @@ -870,7 +994,7 @@ public class XmlConfigurationTest " 100 bas" + ""); - assertThrows(InvocationTargetException.class, () -> xmlConfiguration.configure()); + assertThrows(InvocationTargetException.class, xmlConfiguration::configure); } @Test @@ -937,7 +1061,7 @@ public class XmlConfigurationTest " 1.5" + ""); - assertThrows(InvocationTargetException.class, () -> xmlConfiguration.configure()); + assertThrows(InvocationTargetException.class, xmlConfiguration::configure); } @Test