Merged branch 'jetty-10.0.x' into 'jetty-11.0.x'.
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
commit
f415c5ebea
|
@ -119,29 +119,64 @@ public class XmlConfiguration
|
|||
Class<?> t2 = p2[i].getType();
|
||||
if (t1 != t2)
|
||||
{
|
||||
// Favour derived type over base type
|
||||
compare = Boolean.compare(t1.isAssignableFrom(t2), t2.isAssignableFrom(t1));
|
||||
// prefer primitives
|
||||
compare = Boolean.compare(t2.isPrimitive(), t1.isPrimitive());
|
||||
if (compare == 0)
|
||||
{
|
||||
// favour primitive type over reference
|
||||
compare = Boolean.compare(!t1.isPrimitive(), !t2.isPrimitive());
|
||||
if (compare == 0)
|
||||
// Use name to avoid non determinant sorting
|
||||
compare = t1.getName().compareTo(t2.getName());
|
||||
}
|
||||
// prefer interfaces
|
||||
compare = Boolean.compare(t2.isInterface(), t1.isInterface());
|
||||
|
||||
// break on the first different parameter (should always be true)
|
||||
if (compare == 0)
|
||||
{
|
||||
// prefer most derived
|
||||
int d1 = calculateDepth(t1);
|
||||
int d2 = calculateDepth(t2);
|
||||
compare = Integer.compare(d2, d1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// break on the first different parameter
|
||||
if (compare != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
compare = Math.min(1, Math.max(compare, -1));
|
||||
}
|
||||
|
||||
return compare;
|
||||
// failsafe is to compare on the generic string
|
||||
if (compare == 0)
|
||||
compare = e1.toGenericString().compareTo(e2.toGenericString());
|
||||
|
||||
// Return normalized to -1, 0, 1
|
||||
return Integer.compare(compare, 0);
|
||||
};
|
||||
|
||||
private static int calculateDepth(Class<?> c)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
if (c.isPrimitive())
|
||||
return Integer.MIN_VALUE;
|
||||
|
||||
if (c.isInterface())
|
||||
{
|
||||
Set<Class<?>> interfaces = Set.of(c);
|
||||
while (!interfaces.isEmpty())
|
||||
{
|
||||
depth++;
|
||||
interfaces = interfaces.stream().flatMap(i -> Arrays.stream(i.getInterfaces())).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (c != Object.class && !c.isPrimitive())
|
||||
{
|
||||
depth++;
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the standard IDs and properties expected in a jetty XML file:
|
||||
* <ul>
|
||||
|
@ -932,9 +967,11 @@ public class XmlConfiguration
|
|||
throw new IllegalArgumentException("Method name cannot be blank");
|
||||
|
||||
// Lets just try all methods for now
|
||||
Method[] methods = Arrays.stream(oClass.getMethods())
|
||||
.filter(m -> m.getName().equals(methodName))
|
||||
.sorted(EXECUTABLE_COMPARATOR)
|
||||
.toArray(Method[]::new);
|
||||
|
||||
Method[] methods = oClass.getMethods();
|
||||
Arrays.sort(methods, EXECUTABLE_COMPARATOR);
|
||||
for (Method method : methods)
|
||||
{
|
||||
if (!method.getName().equals(methodName))
|
||||
|
|
|
@ -17,17 +17,22 @@ import java.io.BufferedWriter;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
@ -59,8 +64,10 @@ import org.xml.sax.InputSource;
|
|||
import org.xml.sax.SAXException;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.eclipse.jetty.xml.XmlConfiguration.EXECUTABLE_COMPARATOR;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
@ -728,6 +735,22 @@ public class XmlConfigurationTest
|
|||
{
|
||||
}
|
||||
|
||||
public void call(Aaa aaa)
|
||||
{
|
||||
}
|
||||
|
||||
public void call(Bbb aaa)
|
||||
{
|
||||
}
|
||||
|
||||
public void call(Ccc aaa)
|
||||
{
|
||||
}
|
||||
|
||||
public void call(Abc abc)
|
||||
{
|
||||
}
|
||||
|
||||
public void call(Object o)
|
||||
{
|
||||
}
|
||||
|
@ -745,16 +768,20 @@ public class XmlConfigurationTest
|
|||
}
|
||||
}
|
||||
|
||||
@RepeatedTest(10)
|
||||
@RepeatedTest(100)
|
||||
public void testMethodOrdering() throws Exception
|
||||
{
|
||||
List<Method> methods = Arrays.stream(TestOrder.class.getMethods()).filter(m -> "call".equals(m.getName())).collect(Collectors.toList());
|
||||
Collections.shuffle(methods);
|
||||
methods.sort(XmlConfiguration.EXECUTABLE_COMPARATOR);
|
||||
methods.sort(EXECUTABLE_COMPARATOR);
|
||||
assertThat(methods, Matchers.contains(
|
||||
TestOrder.class.getMethod("call"),
|
||||
TestOrder.class.getMethod("call", int.class),
|
||||
TestOrder.class.getMethod("call", Abc.class),
|
||||
TestOrder.class.getMethod("call", Aaa.class),
|
||||
TestOrder.class.getMethod("call", String.class),
|
||||
TestOrder.class.getMethod("call", Bbb.class),
|
||||
TestOrder.class.getMethod("call", Ccc.class),
|
||||
TestOrder.class.getMethod("call", Object.class),
|
||||
TestOrder.class.getMethod("call", String[].class),
|
||||
TestOrder.class.getMethod("call", String.class, String[].class)
|
||||
|
@ -1927,4 +1954,129 @@ public class XmlConfigurationTest
|
|||
{
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecutableComparator() throws Throwable
|
||||
{
|
||||
aaa(null);
|
||||
bbb(null);
|
||||
ccc(null);
|
||||
|
||||
Stream.of(XmlConfigurationTest.class.getMethods())
|
||||
.filter(m -> m.getName().length() == 3)
|
||||
.filter(m -> !m.getName().equals("foo"))
|
||||
.sorted(EXECUTABLE_COMPARATOR)
|
||||
.map(Executable::toGenericString)
|
||||
.forEach(System.out::println);
|
||||
|
||||
List<Method> methods = Arrays.asList(Arrays.stream(XmlConfigurationTest.class.getMethods())
|
||||
.filter(m -> m.getName().length() == 3)
|
||||
.toArray(Method[]::new));
|
||||
|
||||
// The implementor must also ensure that the relation is transitive: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0
|
||||
assertThat(EXECUTABLE_COMPARATOR.compare(methods.get(0), methods.get(1)), is(EXECUTABLE_COMPARATOR.compare(methods.get(1), methods.get(2))));
|
||||
assertThat(EXECUTABLE_COMPARATOR.compare(methods.get(0), methods.get(1)), is(EXECUTABLE_COMPARATOR.compare(methods.get(0), methods.get(2))));
|
||||
}
|
||||
|
||||
public void aaa(Aaa ignored)
|
||||
{
|
||||
}
|
||||
|
||||
public void bbb(Bbb ignored)
|
||||
{
|
||||
}
|
||||
|
||||
public void ccc(Ccc ignored)
|
||||
{
|
||||
}
|
||||
|
||||
public interface Aaa
|
||||
{
|
||||
}
|
||||
|
||||
public interface Abc extends Aaa
|
||||
{
|
||||
}
|
||||
|
||||
public static class Bbb
|
||||
{
|
||||
}
|
||||
|
||||
public static class Ccc implements Aaa
|
||||
{
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFooExecutableComparator()
|
||||
{
|
||||
List<String> orderedMethodIds = Stream.of(FooObj.class.getMethods())
|
||||
.filter(m -> m.getName().equals("foo"))
|
||||
.sorted(EXECUTABLE_COMPARATOR)
|
||||
.map(Executable::toGenericString)
|
||||
.collect(Collectors.toList());
|
||||
orderedMethodIds.forEach(System.out::println);
|
||||
String[] expectedOrder = {
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo()", // favor fewer args
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(int)", // favor primitives over non-primitives
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(java.time.temporal.Temporal)", // favor over Instant
|
||||
"public int org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(java.lang.String)",
|
||||
"public java.util.Locale org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(java.nio.charset.Charset)",
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(java.time.Instant)",
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(java.util.Locale)",
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(int,java.lang.String)",
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(java.lang.String,int)",
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(int,java.lang.String,java.lang.String)",
|
||||
"public void org.eclipse.jetty.xml.XmlConfigurationTest$FooObj.foo(int,java.lang.String,java.lang.String,java.lang.Object)"
|
||||
};
|
||||
assertThat(orderedMethodIds, contains(expectedOrder));
|
||||
}
|
||||
|
||||
public static class FooObj
|
||||
{
|
||||
public void foo()
|
||||
{
|
||||
}
|
||||
|
||||
public int foo(String name)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void foo(int id)
|
||||
{
|
||||
}
|
||||
|
||||
public void foo(Locale locale)
|
||||
{
|
||||
}
|
||||
|
||||
public void foo(Instant timestamp) // Instant extends from Temporal
|
||||
{
|
||||
}
|
||||
|
||||
public void foo(Temporal temporal)
|
||||
{
|
||||
}
|
||||
|
||||
public Locale foo(Charset charset)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void foo(String name, int id)
|
||||
{
|
||||
}
|
||||
|
||||
public void foo(int id, String name)
|
||||
{
|
||||
}
|
||||
|
||||
public void foo(int id, String name, String description)
|
||||
{
|
||||
}
|
||||
|
||||
public void foo(int id, String name, String description, Object value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue