Merge pull request #4553 from eclipse/jetty-9.4.x-4550-XmlConfiguration-named-args

Fixes #4550 XmlConfiguration named parameters
This commit is contained in:
Simone Bordet 2020-02-20 11:25:00 +01:00 committed by GitHub
commit ea5b70ba37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 496 additions and 307 deletions

View File

@ -24,6 +24,7 @@ import java.io.StringReader;
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;
@ -37,11 +38,12 @@ import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
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 +53,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;
@ -98,6 +99,22 @@ public class XmlConfiguration
};
private static final Iterable<ConfigurationProcessorFactory> __factoryLoader = ServiceLoader.load(ConfigurationProcessorFactory.class);
private static final XmlParser __parser = initParser();
public static final Comparator<Executable> EXECUTABLE_COMPARATOR = (o1, o2) ->
{
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 XmlParser initParser()
{
@ -412,39 +429,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)
@ -916,7 +907,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)
@ -937,7 +927,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());
@ -949,7 +939,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");
@ -957,11 +947,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;
@ -970,34 +964,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)
{
@ -1020,33 +987,16 @@ public class XmlConfiguration
AttrOrElementNode aoeNode = new AttrOrElementNode(obj, node, "Id", "Class", "Arg");
String id = aoeNode.getString("Id");
String clazz = aoeNode.getString("Class");
List<XmlParser.Node> argNodes = aoeNode.getNodes("Arg");
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)
{
@ -1061,80 +1011,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)
{
@ -1777,40 +1667,152 @@ 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 (int i = 0; i < node.size(); 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;
}
/**

View File

@ -41,6 +41,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;
@ -48,6 +60,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

@ -35,6 +35,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
@ -45,6 +46,11 @@ import org.eclipse.jetty.util.resource.PathResource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.xml.sax.SAXException;
import static java.nio.charset.StandardCharsets.UTF_8;
@ -67,9 +73,17 @@ public class XmlConfigurationTest
{
public WorkDir workDir;
protected String[] _configure = new String[]{
"org/eclipse/jetty/xml/configureWithAttr.xml", "org/eclipse/jetty/xml/configureWithElements.xml"
};
public static class ScenarioProvider implements ArgumentsProvider
{
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context)
{
return Stream.of(
"org/eclipse/jetty/xml/configureWithAttr.xml",
"org/eclipse/jetty/xml/configureWithElements.xml"
).map(Arguments::of);
}
}
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 +96,166 @@ public class XmlConfigurationTest
configuration.configure();
}
@Test
public void testPassedObject() throws Exception
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
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);
XmlConfiguration configuration = new XmlConfiguration(url);
TestConfiguration tc = new TestConfiguration("tc");
configuration.getProperties().putAll(properties);
configuration.configure(tc);
Map<String, String> properties = new HashMap<>();
properties.put("whatever", "xxx");
TestConfiguration.VALUE = 77;
URL url = XmlConfigurationTest.class.getClassLoader().getResource(configure);
XmlConfiguration configuration = new XmlConfiguration(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("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(System.getenv("HOME"), tc.get("Env"), "Env");
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("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(tc.testField1, 77, "static to field");
assertEquals(tc.testField2, 2, "field to field");
assertEquals(TestConfiguration.VALUE, 42, "literal to static");
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");
}
assertEquals(((Map<String, String>)configuration.getIdMap().get("map")).get("key0"), "value0");
assertEquals(((Map<String, String>)configuration.getIdMap().get("map")).get("key1"), "value1");
}
@Test
public void testNewObject() throws Exception
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testNewObject(String configure) throws Exception
{
for (String configure : _configure)
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(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(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
@ -648,6 +658,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
{