Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2020-02-20 12:05:27 +01:00
commit f5cfa2bf7d
3 changed files with 570 additions and 402 deletions

View File

@ -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<ConfigurationProcessorFactory> PROCESSOR_FACTORIES = ServiceLoader.load(ConfigurationProcessorFactory.class);
private static final XmlParser PARSER = initParser();
private static final Comparator<Executable> 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<ConfigurationProcessorFactory> __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<String, Object> namedArgMap = new HashMap<>();
List<Object> 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);
}
/**
* <p>Call a setter method.</p>
* <p>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<Object> 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<XmlParser.Node> 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 <Arg> elements
Map<String, Object> namedArgMap = new HashMap<>();
List<Object> 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<String, Object> 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<Object> 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<Object> deprecated = aoeNode.getList("Deprecated");
String dftValue = aoeNode.getString("Default");
// Look for a value
Map<String, String> 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<Object> 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<Object> 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<XmlParser.Node> getNodes(String elementName)
{
String attrName = StringUtil.asciiToLowerCase(elementName);
final List<XmlParser.Node> 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<Object> _arguments;
private final List<String> _names;
private Args(Object obj, Class<?> oClass, List<XmlParser.Node> 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<Object> arguments, List<String> 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<Object> arguments = new ArrayList<>(_arguments);
arguments.add(Array.newInstance(varArgType.getComponentType(), 0));
List<String> 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<String, Integer> 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<Object> arguments = new ArrayList<>(_arguments);
List<String> 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<XmlParser.Node> getNodes(XmlParser.Node node, String elementName)
{
String attrName = StringUtil.asciiToLowerCase(elementName);
final List<XmlParser.Node> 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<LifeCycle> started = new ArrayList<>(objects.size());

View File

@ -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;

View File

@ -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 = "<Array type=\"String\"><Item type=\"String\">String1</Item><Item type=\"String\">String2</Item></Array>";
private static final String INT_ARRAY_XML = "<Array type=\"int\"><Item type=\"int\">1</Item><Item type=\"int\">2</Item></Array>";
@ -82,170 +80,175 @@ public class XmlConfigurationTest
configuration.configure();
}
@Test
public void testPassedObject() throws Exception
public static String[] xmlConfigs()
{
for (String configure : _configure)
{
Map<String, String> 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<String, String>)configuration.getIdMap().get("map")).get("key0"), "value0");
assertEquals(((Map<String, String>)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<String, String> 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<String, String> map = (Map<String, String>)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<String, String> 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<String, String> 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("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\"><Set name=\"PropertyTest\"><Property name=\"null\"/></Set></Configure>");
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
"<New class=\"org.eclipse.jetty.xml.ConstructorArgTestClass\"><Arg type=\"List\">Some String</Arg></New></Configure>");
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("<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\">" +
"<New class=\"org.eclipse.jetty.xml.ConstructorArgTestClass\"><Arg type=\"Set\">Some String</Arg></New></Configure>");
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 + "</Set></Configure>");
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(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Call name=\"setAll\">" +
" <Arg>arg1</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg>arg3</Arg> " +
" </Call>" +
"</Configure>");
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(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Call name=\"setAll\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg name=\"second\">arg2</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" </Call>" +
"</Configure>");
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(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Call name=\"setAll\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" <Arg name=\"second\">arg2</Arg> " +
" </Call>" +
"</Configure>");
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(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Call name=\"setAll\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" </Call>" +
"</Configure>");
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(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Call name=\"setAll\">" +
" <Arg name=\"third\">arg3</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg name=\"first\">arg1</Arg> " +
" </Call>" +
"</Configure>");
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(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Call name=\"setVarArgs\">" +
" <Arg>one</Arg> " +
" <Arg><Array type=\"String\"><Item type=\"String\">two</Item><Item type=\"String\">three</Item></Array></Arg> " +
" </Call>" +
"</Configure>");
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(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg name=\"second\">arg2</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" <Call name=\"setVarArgs\">" +
" <Arg>one</Arg>" +
" </Call>" +
"</Configure>");
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
" <Set name=\"integer\">bad</Set>" +
"</Configure>");
assertThrows(InvocationTargetException.class, () -> xmlConfiguration.configure());
assertThrows(InvocationTargetException.class, xmlConfiguration::configure);
}
@Test
@ -870,7 +994,7 @@ public class XmlConfigurationTest
" <Set name=\"integer\">100 bas</Set>" +
"</Configure>");
assertThrows(InvocationTargetException.class, () -> xmlConfiguration.configure());
assertThrows(InvocationTargetException.class, xmlConfiguration::configure);
}
@Test
@ -937,7 +1061,7 @@ public class XmlConfigurationTest
" <Set name=\"integer\">1.5</Set>" +
"</Configure>");
assertThrows(InvocationTargetException.class, () -> xmlConfiguration.configure());
assertThrows(InvocationTargetException.class, xmlConfiguration::configure);
}
@Test