[Bug 380924] xmlconfiguration <Configure and <New supports named constructors, including dynamic ordering of parameters

This commit is contained in:
Jesse McConnell 2012-08-10 15:44:08 -05:00
parent b5f7e054be
commit 6d3d7b0df8
5 changed files with 271 additions and 5 deletions

View File

@ -15,6 +15,7 @@ package org.eclipse.jetty.util;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -23,7 +24,9 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
@ -565,4 +568,65 @@ public class TypeUtil
} }
throw new NoSuchMethodException("<init>"); throw new NoSuchMethodException("<init>");
} }
public static Object construct(Class<?> klass, Object[] arguments, Map<String, Object> namedArgMap) throws InvocationTargetException, NoSuchMethodException
{
for (Constructor<?> constructor : klass.getConstructors())
{
if (constructor.getParameterTypes().length != arguments.length)
continue;
try
{
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
// target has no annotations
if ( parameterAnnotations == null || parameterAnnotations.length == 0 )
{
LOG.debug("Target has no parameter annotations");
return constructor.newInstance(arguments);
}
else
{
Object[] swizzled = new Object[arguments.length];
int count = 0;
for ( Annotation[] annotations : parameterAnnotations )
{
for ( Annotation annotation : annotations)
{
if ( annotation instanceof Name )
{
Name param = (Name)annotation;
if (namedArgMap.containsKey(param.value()))
{
LOG.debug("placing named {} in position {}", param.value(), count);
swizzled[count] = namedArgMap.get(param.value());
}
else
{
LOG.debug("placing {} in position {}", arguments[count], count);
swizzled[count] = arguments[count];
}
++count;
}
else
{
LOG.debug("passing on annotation {}", annotation);
}
}
}
return constructor.newInstance(swizzled);
}
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException e)
{
LOG.ignore(e);
}
}
throw new NoSuchMethodException("<init>");
}
} }

View File

@ -33,6 +33,7 @@ import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@ -49,6 +50,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlParser.Node; import org.eclipse.jetty.xml.XmlParser.Node;
import org.junit.Assert;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -341,29 +343,50 @@ public class XmlConfiguration
String id = _config.getAttribute("id"); String id = _config.getAttribute("id");
Object obj = id == null ? null : _idMap.get(id); Object obj = id == null ? null : _idMap.get(id);
int index = _config.size(); int index = _config.size();
if (obj == null && oClass != null) if (obj == null && oClass != null)
{ {
List<Object> arguments = new ArrayList<>(); Map<String, Object> namedArgMap = new HashMap<String, Object>();
List<Object> arguments = new LinkedList<>();
for (int i = 0; i < _config.size(); i++) for (int i = 0; i < _config.size(); i++)
{ {
Object o = _config.get(i); Object o = _config.get(i);
if (o instanceof String) if (o instanceof String)
{
continue; continue;
if (!((XmlParser.Node)o).getTag().equals("Arg")) }
XmlParser.Node node = (XmlParser.Node)o;
if (!(node.getTag().equals("Arg")))
{ {
index = i; index = i;
break; break;
} }
else else
{ {
String namedAttribute = node.getAttribute("name");
if ( namedAttribute != null )
{
namedArgMap.put(namedAttribute,value(obj,(XmlParser.Node)o));
}
arguments.add(value(obj, (XmlParser.Node)o)); arguments.add(value(obj, (XmlParser.Node)o));
} }
} }
try try
{ {
obj = TypeUtil.construct(oClass, arguments.toArray()); if ( namedArgMap.size() > 0 )
{
obj = TypeUtil.construct(oClass, arguments.toArray(), namedArgMap);
}
else
{
obj = TypeUtil.construct(oClass, arguments.toArray());
}
} }
catch (NoSuchMethodException x) catch (NoSuchMethodException x)
{ {
@ -780,13 +803,29 @@ public class XmlConfiguration
size++; size++;
} }
Map<String, Object> namedArgMap = new HashMap<String, Object>();
List<Object> arguments = new LinkedList<>();
Object[] arg = new Object[size]; Object[] arg = new Object[size];
for (int i = 0, j = 0; j < size; i++) for (int i = 0, j = 0; j < size; i++)
{ {
Object o = node.get(i); Object o = node.get(i);
XmlParser.Node argNode = (XmlParser.Node)o;
if (o instanceof String) if (o instanceof String)
{
continue; continue;
}
arg[j++] = value(obj,(XmlParser.Node)o); arg[j++] = value(obj,(XmlParser.Node)o);
String namedAttribute = argNode.getAttribute("name");
if ( namedAttribute != null )
{
namedArgMap.put(namedAttribute,value(obj,(XmlParser.Node)o));
}
arguments.add(value(obj,(XmlParser.Node)o));
} }
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -794,7 +833,18 @@ public class XmlConfiguration
try try
{ {
Object n = TypeUtil.construct(oClass, arg); Object n;
if ( namedArgMap.size() > 0 )
{
LOG.debug("using named mapping");
n = TypeUtil.construct(oClass, arguments.toArray(), namedArgMap);
}
else
{
LOG.debug("using normal mapping");
n = TypeUtil.construct(oClass, arguments.toArray());
}
configure(n,node,argi); configure(n,node,argi);
return n; return n;
} }

View File

@ -8,6 +8,8 @@ public class AnnotatedTestConfiguration
private String second; private String second;
private String third; private String third;
AnnotatedTestConfiguration nested;
public AnnotatedTestConfiguration(@Name("first") String first, @Name("second") String second, @Name("third") String third) public AnnotatedTestConfiguration(@Name("first") String first, @Name("second") String second, @Name("third") String third)
{ {
this.first = first; this.first = first;
@ -45,6 +47,14 @@ public class AnnotatedTestConfiguration
this.third = third; this.third = third;
} }
public AnnotatedTestConfiguration getNested()
{
return nested;
}
public void setNested(AnnotatedTestConfiguration nested)
{
this.nested = nested;
}
} }

View File

@ -377,6 +377,7 @@ public class XmlConfigurationTest
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird()); Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
} }
@Test
public void testConstructorNamedInjectionUnOrdered() throws Exception public void testConstructorNamedInjectionUnOrdered() throws Exception
{ {
XmlConfiguration xmlConfiguration = new XmlConfiguration("" + XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
@ -393,6 +394,7 @@ public class XmlConfigurationTest
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird()); Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
} }
@Test
public void testConstructorNamedInjectionOrderedMixed() throws Exception public void testConstructorNamedInjectionOrderedMixed() throws Exception
{ {
XmlConfiguration xmlConfiguration = new XmlConfiguration("" + XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
@ -409,6 +411,7 @@ public class XmlConfigurationTest
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird()); Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
} }
@Test
public void testConstructorNamedInjectionUnorderedMixed() throws Exception public void testConstructorNamedInjectionUnorderedMixed() throws Exception
{ {
XmlConfiguration xmlConfiguration = new XmlConfiguration("" + XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
@ -424,4 +427,140 @@ public class XmlConfigurationTest
Assert.assertEquals("second parameter not wired correctly","arg2", atc.getSecond()); Assert.assertEquals("second parameter not wired correctly","arg2", atc.getSecond());
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird()); Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
} }
@Test
public void testNestedConstructorNamedInjection() throws Exception
{
XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg>arg1</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg>arg3</Arg> " +
" <Set name=\"nested\"> " +
" <New class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg>arg1</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg>arg3</Arg> " +
" </New>" +
" </Set>" +
"</Configure>");
AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure();
Assert.assertEquals("first parameter not wired correctly","arg1", atc.getFirst());
Assert.assertEquals("second parameter not wired correctly","arg2", atc.getSecond());
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
}
@Test
public void testNestedConstructorNamedInjectionOrdered() throws Exception
{
XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg name=\"second\">arg2</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" <Set name=\"nested\"> " +
" <New class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg name=\"second\">arg2</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" </New>" +
" </Set>" +
"</Configure>");
AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure();
Assert.assertEquals("first parameter not wired correctly","arg1", atc.getFirst());
Assert.assertEquals("second parameter not wired correctly","arg2", atc.getSecond());
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
}
@Test
public void testNestedConstructorNamedInjectionUnOrdered() throws Exception
{
XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" <Arg name=\"second\">arg2</Arg> " +
" <Set name=\"nested\"> " +
" <New class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" <Arg name=\"second\">arg2</Arg> " +
" </New>" +
" </Set>" +
"</Configure>");
AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure();
Assert.assertEquals("first parameter not wired correctly","arg1", atc.getFirst());
Assert.assertEquals("second parameter not wired correctly","arg2", atc.getSecond());
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
}
@Test
public void testNestedConstructorNamedInjectionOrderedMixed() throws Exception
{
XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" <Set name=\"nested\"> " +
" <New class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"first\">arg1</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg name=\"third\">arg3</Arg> " +
" </New>" +
" </Set>" +
"</Configure>");
AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure();
Assert.assertEquals("first parameter not wired correctly","arg1", atc.getFirst());
Assert.assertEquals("second parameter not wired correctly","arg2", atc.getSecond());
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
}
@Test
public void testNestedConstructorNamedInjectionUnorderedMixed() throws Exception
{
XmlConfiguration xmlConfiguration = new XmlConfiguration("" +
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"third\">arg3</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg name=\"first\">arg1</Arg> " +
" <Set name=\"nested\"> " +
" <New class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Arg name=\"third\">arg3</Arg> " +
" <Arg>arg2</Arg> " +
" <Arg name=\"first\">arg1</Arg> " +
" </New>" +
" </Set>" +
"</Configure>");
AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure();
Assert.assertEquals("first parameter not wired correctly","arg1", atc.getFirst());
Assert.assertEquals("second parameter not wired correctly","arg2", atc.getSecond());
Assert.assertEquals("third parameter not wired correctly","arg3", atc.getThird());
Assert.assertEquals("nested first parameter not wired correctly","arg1", atc.getNested().getFirst());
Assert.assertEquals("nested second parameter not wired correctly","arg2", atc.getNested().getSecond());
Assert.assertEquals("nested third parameter not wired correctly","arg3", atc.getNested().getThird());
}
} }

View File

@ -0,0 +1,3 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.xml.LEVEL=DEBUG
org.eclipse.jetty.util.LEVEL=DEBUG