Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

This commit is contained in:
Greg Wilkins 2020-02-26 14:36:22 +01:00
commit 277ce5f3a5
6 changed files with 217 additions and 50 deletions

View File

@ -199,7 +199,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
private final List<EventListener> _programmaticListeners = new CopyOnWriteArrayList<>(); private final List<EventListener> _programmaticListeners = new CopyOnWriteArrayList<>();
private final List<ServletContextListener> _servletContextListeners = new CopyOnWriteArrayList<>(); private final List<ServletContextListener> _servletContextListeners = new CopyOnWriteArrayList<>();
private final List<ServletContextListener> _destroySerletContextListeners = new ArrayList<>(); private final List<ServletContextListener> _destroyServletContextListeners = new ArrayList<>();
private final List<ServletContextAttributeListener> _servletContextAttributeListeners = new CopyOnWriteArrayList<>(); private final List<ServletContextAttributeListener> _servletContextAttributeListeners = new CopyOnWriteArrayList<>();
private final List<ServletRequestListener> _servletRequestListeners = new CopyOnWriteArrayList<>(); private final List<ServletRequestListener> _servletRequestListeners = new CopyOnWriteArrayList<>();
private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners = new CopyOnWriteArrayList<>(); private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners = new CopyOnWriteArrayList<>();
@ -646,7 +646,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
_contextListeners.remove(listener); _contextListeners.remove(listener);
if (listener instanceof ServletContextListener) if (listener instanceof ServletContextListener)
{
_servletContextListeners.remove(listener); _servletContextListeners.remove(listener);
_destroyServletContextListeners.remove(listener);
}
if (listener instanceof ServletContextAttributeListener) if (listener instanceof ServletContextAttributeListener)
_servletContextAttributeListeners.remove(listener); _servletContextAttributeListeners.remove(listener);
@ -838,14 +841,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
super.doStart(); super.doStart();
// Call context listeners // Call context listeners
_destroySerletContextListeners.clear(); _destroyServletContextListeners.clear();
if (!_servletContextListeners.isEmpty()) if (!_servletContextListeners.isEmpty())
{ {
ServletContextEvent event = new ServletContextEvent(_scontext); ServletContextEvent event = new ServletContextEvent(_scontext);
for (ServletContextListener listener : _servletContextListeners) for (ServletContextListener listener : _servletContextListeners)
{ {
callContextInitialized(listener, event); callContextInitialized(listener, event);
_destroySerletContextListeners.add(listener); _destroyServletContextListeners.add(listener);
} }
} }
} }
@ -854,9 +857,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{ {
// Call the context listeners // Call the context listeners
ServletContextEvent event = new ServletContextEvent(_scontext); ServletContextEvent event = new ServletContextEvent(_scontext);
Collections.reverse(_destroySerletContextListeners); Collections.reverse(_destroyServletContextListeners);
MultiException ex = new MultiException(); MultiException ex = new MultiException();
for (ServletContextListener listener : _destroySerletContextListeners) for (ServletContextListener listener : _destroyServletContextListeners)
{ {
try try
{ {

View File

@ -239,7 +239,7 @@ public class DateCache
// recheck the tick, to save multiple formats // recheck the tick, to save multiple formats
if (tick == null || tick._seconds != seconds) if (tick == null || tick._seconds != seconds)
{ {
String s = ZonedDateTime.ofInstant(Instant.now(), _zoneId).format(_tzFormat); String s = ZonedDateTime.ofInstant(Instant.ofEpochMilli(now), _zoneId).format(_tzFormat);
_tick = new Tick(seconds, s); _tick = new Tick(seconds, s);
tick = _tick; tick = _tick;
} }

View File

@ -28,6 +28,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@ -97,20 +98,47 @@ public class XmlConfiguration
}; };
private static final Iterable<ConfigurationProcessorFactory> PROCESSOR_FACTORIES = ServiceLoader.load(ConfigurationProcessorFactory.class); private static final Iterable<ConfigurationProcessorFactory> PROCESSOR_FACTORIES = ServiceLoader.load(ConfigurationProcessorFactory.class);
private static final XmlParser PARSER = initParser(); private static final XmlParser PARSER = initParser();
private static final Comparator<Executable> EXECUTABLE_COMPARATOR = (o1, o2) -> public static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) ->
{ {
int p1 = o1.getParameterCount(); // Favour methods with less parameters
int p2 = o2.getParameterCount(); int count = e1.getParameterCount();
int compare = Integer.compare(p1, p2); int compare = Integer.compare(count, e2.getParameterCount());
if (compare == 0 && p1 > 0) if (compare == 0 && count > 0)
{ {
boolean a1 = o1.getParameterTypes()[p1 - 1].isArray(); Parameter[] p1 = e1.getParameters();
boolean a2 = o2.getParameterTypes()[p2 - 1].isArray(); Parameter[] p2 = e2.getParameters();
if (a1 && !a2)
compare = 1; // Favour methods without varargs
else if (!a1 && a2) compare = Boolean.compare(p1[count - 1].isVarArgs(), p2[count - 1].isVarArgs());
compare = -1; if (compare == 0)
{
// Rank by differences in the parameters
for (int i = 0; i < count; i++)
{
Class<?> t1 = p1[i].getType();
Class<?> t2 = p2[i].getType();
if (t1 != t2)
{
// Favour derived type over base type
compare = Boolean.compare(t1.isAssignableFrom(t2), t2.isAssignableFrom(t1));
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());
}
// break on the first different parameter (should always be true)
if (compare != 0)
break;
}
}
}
compare = Math.min(1, Math.max(compare, -1));
} }
return compare; return compare;
}; };
@ -1610,7 +1638,7 @@ public class XmlConfiguration
{ {
// Could this be an empty varargs match? // Could this be an empty varargs match?
int count = executable.getParameterCount(); int count = executable.getParameterCount();
if (count > 0 && executable.getParameterTypes()[count - 1].isArray()) if (count > 0 && executable.getParameters()[count - 1].isVarArgs())
{ {
// There is not a no varArgs alternative so let's try a an empty varArgs match // There is not a no varArgs alternative so let's try a an empty varArgs match
args = asEmptyVarArgs(executable.getParameterTypes()[count - 1]).matchArgsToParameters(executable); args = asEmptyVarArgs(executable.getParameterTypes()[count - 1]).matchArgsToParameters(executable);
@ -1641,47 +1669,54 @@ public class XmlConfiguration
return new Object[0]; return new Object[0];
// If no arg names are specified, keep the arg order // If no arg names are specified, keep the arg order
Object[] args;
if (_names.stream().noneMatch(Objects::nonNull)) 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++; args = _arguments.toArray(new Object[0]);
Arrays.stream(paramAnnotation)
.filter(Name.class::isInstance)
.map(Name.class::cast)
.findFirst().ifPresent(n -> position.put(n.value(), pos));
} }
else
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 we don't have any parameters with names, then no match
if (name != null) 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 = position.get(name); Integer pos = p++;
if (pos == null) Arrays.stream(paramAnnotation)
return null; .filter(Name.class::isInstance)
if (pos != p) .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)
{ {
// adjust position of parameter Integer pos = position.get(name);
arguments.add(pos, arguments.remove(p)); if (pos == null)
names.add(pos, names.remove(p)); return null;
p = Math.min(p, pos); if (pos != p)
{
// adjust position of parameter
arguments.add(pos, arguments.remove(p));
names.add(pos, names.remove(p));
p = Math.min(p, pos);
}
} }
} }
args = arguments.toArray(new Object[0]);
} }
return arguments.toArray(new Object[0]);
return args;
} }
} }
} }

View File

@ -88,6 +88,21 @@ public class AnnotatedTestConfiguration
this.third = theRest.length > 1 ? theRest[1] : null; this.third = theRest.length > 1 ? theRest[1] : null;
} }
public void call(Integer value)
{
this.first = String.valueOf(value);
}
public void call(String value)
{
this.second = value;
}
public <E> void call(E value)
{
this.third = String.valueOf(value);
}
public String getFirst() public String getFirst()
{ {
return first; return first;

View File

@ -54,6 +54,7 @@ public class TestConfiguration extends HashMap<String, Object>
private Set set; private Set set;
private ConstructorArgTestClass constructorArgTestClass; private ConstructorArgTestClass constructorArgTestClass;
public Map map; public Map map;
public Double number;
public TestConfiguration() public TestConfiguration()
{ {
@ -65,6 +66,16 @@ public class TestConfiguration extends HashMap<String, Object>
name = n; name = n;
} }
public void setNumber(Object value)
{
testObject = value;
}
public void setNumber(double value)
{
number = value;
}
public void setTest(Object value) public void setTest(Object value)
{ {
testObject = value; testObject = value;

View File

@ -23,10 +23,12 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
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 java.util.Map;
@ -41,11 +43,14 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog; import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
@ -633,6 +638,72 @@ public class XmlConfigurationTest
assertEquals("arg3", atc.getNested().getThird(), "nested third parameter not wired correctly"); assertEquals("arg3", atc.getNested().getThird(), "nested third parameter not wired correctly");
} }
private static class TestOrder
{
public void call()
{
}
public void call(int o)
{
}
public void call(Object o)
{
}
public void call(String s)
{
}
public void call(String... ss)
{
}
public void call(String s, String... ss)
{
}
}
@RepeatedTest(10)
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);
Collections.sort(methods, XmlConfiguration.EXECUTABLE_COMPARATOR);
assertThat(methods, Matchers.contains(
TestOrder.class.getMethod("call"),
TestOrder.class.getMethod("call", int.class),
TestOrder.class.getMethod("call", String.class),
TestOrder.class.getMethod("call", Object.class),
TestOrder.class.getMethod("call", String[].class),
TestOrder.class.getMethod("call", String.class, String[].class)
));
}
@Test
public void testOverloadedCall() throws Exception
{
XmlConfiguration xmlConfiguration = asXmlConfiguration(
"<Configure class=\"org.eclipse.jetty.xml.AnnotatedTestConfiguration\">" +
" <Call name=\"call\">" +
" <Arg type=\"int\">1</Arg>" +
" </Call>" +
" <Call name=\"call\">" +
" <Arg>2</Arg>" +
" </Call>" +
" <Call name=\"call\">" +
" <Arg type=\"Long\">3</Arg>" +
" </Call>" +
"</Configure>");
AnnotatedTestConfiguration atc = (AnnotatedTestConfiguration)xmlConfiguration.configure();
assertEquals("1", atc.getFirst());
assertEquals("2", atc.getSecond());
assertEquals("3", atc.getThird());
}
@Test @Test
public void testNestedConstructorNamedInjectionUnOrdered() throws Exception public void testNestedConstructorNamedInjectionUnOrdered() throws Exception
{ {
@ -820,6 +891,38 @@ public class XmlConfigurationTest
assertNull(atc.getThird()); assertNull(atc.getThird());
} }
public static List<String> typeTestData()
{
return Arrays.asList(
"byte",
"int",
"short",
"long",
"float",
"double",
"Byte",
"Integer",
"Short",
"Long",
"Float",
"Double");
}
@ParameterizedTest
@MethodSource("typeTestData")
public void testCallNumberConversion(String type) throws Exception
{
XmlConfiguration xmlConfiguration = asXmlConfiguration(
"<Configure class=\"org.eclipse.jetty.xml.TestConfiguration\">" +
" <Call name=\"setNumber\">" +
" <Arg type=\"" + type + "\">42</Arg>" +
" </Call>" +
"</Configure>");
TestConfiguration tc = (TestConfiguration)xmlConfiguration.configure();
assertEquals(42.0D, tc.number);
}
@Test @Test
public void testArgumentsGetIgnoredMissingDTD() throws Exception public void testArgumentsGetIgnoredMissingDTD() throws Exception
{ {