Merge remote-tracking branch 'eclipse/jetty-9.4.x' into jetty-9.4.x-stackTraceRemoval

This commit is contained in:
Lachlan Roberts 2018-05-29 10:57:51 +10:00
commit 52ffe24389
11 changed files with 178 additions and 77 deletions

2
.gitignore vendored
View File

@ -46,4 +46,4 @@ bin/
# test generated content
*/src/test/*/WEB-INF/lib/test*.jar
.flattened-pom.xml

View File

@ -206,18 +206,6 @@ xmlns:date="http://exslt.org/dates-and-times"
</span>
</div>
</p>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1149868-7']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</xsl:template>
<!--

View File

@ -28,6 +28,7 @@ import java.nio.channels.WritePendingException;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.thread.Invocable;
/**
*
@ -200,7 +201,8 @@ public interface EndPoint extends Closeable
/**
* <p>Requests callback methods to be invoked when a call to {@link #fill(ByteBuffer)} would return data or EOF.</p>
*
* @param callback the callback to call when an error occurs or we are readable.
* @param callback the callback to call when an error occurs or we are readable. The callback may implement the {@link Invocable} interface to
* self declare its blocking status. Non-blocking callbacks may be called more efficiently without dispatch delays.
* @throws ReadPendingException if another read operation is concurrent.
*/
void fillInterested(Callback callback) throws ReadPendingException;
@ -208,7 +210,8 @@ public interface EndPoint extends Closeable
/**
* <p>Requests callback methods to be invoked when a call to {@link #fill(ByteBuffer)} would return data or EOF.</p>
*
* @param callback the callback to call when an error occurs or we are readable.
* @param callback the callback to call when an error occurs or we are readable. The callback may implement the {@link Invocable} interface to
* self declare its blocking status. Non-blocking callbacks may be called more efficiently without dispatch delays.
* @return true if set
*/
boolean tryFillInterested(Callback callback);
@ -223,7 +226,8 @@ public interface EndPoint extends Closeable
* <p>Writes the given buffers via {@link #flush(ByteBuffer...)} and invokes callback methods when either
* all the data has been flushed or an error occurs.</p>
*
* @param callback the callback to call when an error occurs or the write completed.
* @param callback the callback to call when an error occurs or the write completed. The callback may implement the {@link Invocable} interface to
* self declare its blocking status. Non-blocking callbacks may be called more efficiently without dispatch delays.
* @param buffers one or more {@link ByteBuffer}s that will be flushed.
* @throws WritePendingException if another write operation is concurrent.
*/

View File

@ -69,15 +69,6 @@ import org.eclipse.jetty.util.thread.Scheduler;
* </p>
* <h2>Selectors</h2>
* <p>
* The connector will use the {@link Executor} service to execute a number of Selector Tasks,
* which are implemented to each use a NIO {@link Selector} instance to asynchronously
* schedule a set of accepted connections. It is the selector thread that will call the
* {@link Callback} instances passed in the {@link EndPoint#fillInterested(Callback)} or
* {@link EndPoint#write(Callback, java.nio.ByteBuffer...)} methods. It is expected
* that these callbacks may do some non-blocking IO work, but will always dispatch to the
* {@link Executor} service any blocking, long running or application tasks.
* </p>
* <p>
* The default number of selectors is equal to half of the number of processors available to the JVM,
* which should allow optimal performance even if all the connections used are performing
* significant non-blocking work in the callback tasks.

View File

@ -43,6 +43,8 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.hamcrest.Matchers;
import org.junit.Assert;
@ -55,6 +57,8 @@ import org.junit.runner.RunWith;
@RunWith(AdvancedRunner.class)
public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
{
protected static final Logger LOG = Log.getLogger(ConnectorTimeoutTest.class);
@Rule
public TestTracker tracker = new TestTracker();
@ -610,11 +614,17 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
}
catch(SSLException e)
{
e.printStackTrace();
if(LOG.isDebugEnabled())
LOG.debug(e);
else
LOG.info(e.getMessage());
}
catch(Exception e)
{
e.printStackTrace();
if(LOG.isDebugEnabled())
LOG.debug(e);
else
LOG.info(e.getMessage());
}
Assert.assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start < maximumTestRuntime);
@ -639,11 +649,17 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
}
catch(SSLException e)
{
e.printStackTrace();
if(LOG.isDebugEnabled())
LOG.debug(e);
else
LOG.info(e.getMessage());
}
catch(Exception e)
{
e.printStackTrace();
if(LOG.isDebugEnabled())
LOG.debug(e);
else
LOG.info(e.getMessage());
}
Assert.assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start < maximumTestRuntime);

View File

@ -19,11 +19,9 @@
package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
@ -39,6 +37,10 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
@RunWith(AdvancedRunner.class)
public class LowResourcesMonitorTest
{
@ -216,32 +218,67 @@ public class LowResourcesMonitorTest
}
@Test
public void testMaxLowResourceTime() throws Exception
public void testMaxLowResourcesTime() throws Exception
{
_lowResourcesMonitor.setMaxLowResourcesTime(5000);
int monitorPeriod = _lowResourcesMonitor.getPeriod();
int lowResourcesIdleTimeout = _lowResourcesMonitor.getLowResourcesIdleTimeout();
Assert.assertThat(lowResourcesIdleTimeout, Matchers.lessThan(monitorPeriod));
int maxLowResourcesTime = 5 * monitorPeriod;
_lowResourcesMonitor.setMaxLowResourcesTime(maxLowResourcesTime);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
try(Socket socket0 = new Socket("localhost",_connector.getLocalPort()))
{
// Put the lowResourceMonitor in low mode.
_lowResourcesMonitor.setMaxMemory(1);
Thread.sleep(2400);
// Wait a couple of monitor periods so that
// lowResourceMonitor detects it is in low mode.
Thread.sleep(2 * monitorPeriod);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
// We already waited enough for lowResourceMonitor to close socket0.
Assert.assertEquals(-1, socket0.getInputStream().read());
// New connections are not affected by the
// low mode until maxLowResourcesTime elapses.
try(Socket socket1 = new Socket("localhost",_connector.getLocalPort()))
{
Thread.sleep(2400);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
Assert.assertEquals(-1,socket0.getInputStream().read());
socket1.getOutputStream().write("G".getBytes(StandardCharsets.UTF_8));
// Set a very short read timeout so we can test if the server closed.
socket1.setSoTimeout(1);
InputStream input1 = socket1.getInputStream();
Thread.sleep(2400);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
socket1.getOutputStream().write("E".getBytes(StandardCharsets.UTF_8));
try
{
input1.read();
Assert.fail();
}
catch (SocketTimeoutException expected)
{
}
Thread.sleep(2400);
// Wait a couple of lowResources idleTimeouts.
Thread.sleep(2 * lowResourcesIdleTimeout);
// Verify the new socket is still open.
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
Assert.assertEquals(-1,socket1.getInputStream().read());
try
{
input1.read();
Assert.fail();
}
catch (SocketTimeoutException expected)
{
}
// Let the maxLowResourcesTime elapse.
Thread.sleep(maxLowResourcesTime);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
// Now also the new socket should be closed.
Assert.assertEquals(-1, input1.read());
}
}
}

View File

@ -37,6 +37,7 @@ import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
@ -134,7 +135,7 @@ public class ServerConnectorTimeoutTest extends ConnectorTimeoutTest
@Test
public void testHttpWriteIdleTimeout() throws Exception
{
_httpConfiguration.setBlockingTimeout(500);
_httpConfiguration.setIdleTimeout(500);
configureServer(new AbstractHandler.ErrorDispatchHandler()
{
@Override

View File

@ -53,12 +53,13 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
public AnnotatedServerEndpointConfig(WebSocketContainerScope containerScope, Class<?> endpointClass, ServerEndpoint anno, ServerEndpointConfig baseConfig) throws DeploymentException
{
ServerEndpointConfig.Configurator configr = null;
// A manually declared Configurator (not the one from the annotation)
ServerEndpointConfig.Configurator manualConfigurator = null;
// Copy from base config
if (baseConfig != null)
{
configr = baseConfig.getConfigurator();
manualConfigurator = baseConfig.getConfigurator();
}
// Decoders (favor provided config over annotation)
@ -112,24 +113,24 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
userProperties.putAll(baseConfig.getUserProperties());
}
ServerEndpointConfig.Configurator cfgr;
ServerEndpointConfig.Configurator resolvedConfigurator;
if (anno.configurator() == ServerEndpointConfig.Configurator.class)
// Use ServerEndpointConfig provided configurator if declared
if ( (manualConfigurator != null) && !(manualConfigurator instanceof ContainerDefaultConfigurator) )
{
if (configr != null)
{
cfgr = configr;
}
else
{
cfgr = new ContainerDefaultConfigurator();
}
resolvedConfigurator = manualConfigurator;
}
// Use Container Default if annotation based configurator is undeclared
else if (anno.configurator() == ServerEndpointConfig.Configurator.class)
{
resolvedConfigurator = new ContainerDefaultConfigurator();
}
// Use annotation declared configurator
else
{
try
{
cfgr = anno.configurator().getDeclaredConstructor( ).newInstance();
resolvedConfigurator = anno.configurator().getDeclaredConstructor( ).newInstance();
}
catch (Exception e)
{
@ -143,7 +144,7 @@ public class AnnotatedServerEndpointConfig implements ServerEndpointConfig
}
// Make sure all Configurators obtained are decorated
this.configurator = containerScope.getObjectFactory().decorate(cfgr);
this.configurator = containerScope.getObjectFactory().decorate(resolvedConfigurator);
}
@Override

View File

@ -362,6 +362,35 @@ public class ConfiguratorTest
}
}
public static class ConfigNormalConfigurator extends ServerEndpointConfig.Configurator
{
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
{
sec.getUserProperties().put("self.configurator", this.getClass().getName());
}
}
public static class ConfigOverrideConfigurator extends ServerEndpointConfig.Configurator
{
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
{
sec.getUserProperties().put("self.configurator", this.getClass().getName());
}
}
@ServerEndpoint(value = "/config-normal",
configurator = ConfigNormalConfigurator.class)
public static class ConfigNormalSocket
{
@OnMessage
public String onMessage(Session session, String msg)
{
return String.format("UserProperties[self.configurator] = %s", session.getUserProperties().get("self.configurator"));
}
}
private static BlockheadClient client;
private static Server server;
private static URI baseServerUri;
@ -387,6 +416,13 @@ public class ConfiguratorTest
container.addEndpoint(AddressSocket.class);
container.addEndpoint(TimeDecoderSocket.class);
container.addEndpoint(ConfigNormalSocket.class);
ServerEndpointConfig overrideEndpointConfig = ServerEndpointConfig.Builder
.create(ConfigNormalSocket.class, "/config-override")
.configurator(new ConfigOverrideConfigurator())
.build();
container.addEndpoint(overrideEndpointConfig);
server.start();
String host = connector.getHost();
if (host == null)
@ -667,4 +703,44 @@ public class ConfiguratorTest
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("cal=2016.06.20 AD at 14:27:44 +0000"));
}
}
/**
* Test that a Configurator declared in the annotation is used
* @throws Exception
*/
@Test
public void testAnnotationConfigurator() throws Exception
{
URI uri = baseServerUri.resolve("/config-normal");
BlockheadClientRequest request = client.newWsRequest(uri);
Future<BlockheadConnection> connFut = request.sendAsync();
try (BlockheadConnection clientConn = connFut.get(Timeouts.CONNECT, Timeouts.CONNECT_UNIT))
{
clientConn.write(new TextFrame().setPayload("tellme"));
LinkedBlockingQueue<WebSocketFrame> frames = clientConn.getFrameQueue();
WebSocketFrame frame = frames.poll(Timeouts.POLL_EVENT, Timeouts.POLL_EVENT_UNIT);
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("UserProperties[self.configurator] = " + ConfigNormalConfigurator.class.getName()));
}
}
/**
* Test that a provided ServerEndpointConfig can override the annotation Configurator
* @throws Exception
*/
@Test
public void testOverrideConfigurator() throws Exception
{
URI uri = baseServerUri.resolve("/config-override");
BlockheadClientRequest request = client.newWsRequest(uri);
Future<BlockheadConnection> connFut = request.sendAsync();
try (BlockheadConnection clientConn = connFut.get(Timeouts.CONNECT, Timeouts.CONNECT_UNIT))
{
clientConn.write(new TextFrame().setPayload("tellme"));
LinkedBlockingQueue<WebSocketFrame> frames = clientConn.getFrameQueue();
WebSocketFrame frame = frames.poll(Timeouts.POLL_EVENT, Timeouts.POLL_EVENT_UNIT);
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("UserProperties[self.configurator] = " + ConfigOverrideConfigurator.class.getName()));
}
}
}

View File

@ -52,11 +52,12 @@ public class PongContextListener implements ServletContextListener
try
{
Configurator config = new Config();
// Use manually declared Configurator
container.addEndpoint(ServerEndpointConfig.Builder.create(PongMessageEndpoint.class,"/ping").configurator(config).build());
container.addEndpoint(ServerEndpointConfig.Builder.create(PongMessageEndpoint.class,"/pong").configurator(config).build());
// Use annotation declared Configurator
container.addEndpoint(ServerEndpointConfig.Builder.create(PongSocket.class,"/ping-socket").build());
container.addEndpoint(ServerEndpointConfig.Builder.create(PongSocket.class,"/pong-socket").build());
}
catch (DeploymentException e)
{

18
pom.xml
View File

@ -31,7 +31,7 @@
<jmhjar.name>benchmarks</jmhjar.name>
<surefireVersion>2.21.0</surefireVersion>
<mavenPluginToolsVersion>3.5</mavenPluginToolsVersion>
<mavenPluginToolsVersion>3.5.2</mavenPluginToolsVersion>
<mavenVersion>3.5.0</mavenVersion>
</properties>
@ -477,7 +477,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<configuration>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
@ -1293,20 +1293,6 @@
<configuration>
<!-- needed for Java 8+ -->
<additionalparam>&#45;&#45;allow-script-in-comments</additionalparam>
<header>
<![CDATA[
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1149868-7']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
]]>
</header>
</configuration>
</plugin>
</plugins>