org.restlet
org.restlet
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpClientHelper.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpClientHelper.java
index 696591962e..6e534e77d5 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpClientHelper.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpClientHelper.java
@@ -1,22 +1,15 @@
/**
* Copyright 2005-2024 Qlik
- *
+ *
* The contents of this file is subject to the terms of the Apache 2.0 open
* source license available at http://www.opensource.org/licenses/apache-2.0
- *
+ *
* Restlet is a registered trademark of QlikTech International AB.
*/
package org.restlet.ext.jetty;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.concurrent.Executor;
-import java.util.logging.Level;
-
import org.eclipse.jetty.client.AuthenticationStore;
-import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpProxy;
@@ -49,6 +42,12 @@
import org.restlet.ext.jetty.internal.JettyClientCall;
import org.restlet.ext.jetty.internal.RestletSslContextFactoryClient;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.concurrent.Executor;
+import java.util.logging.Level;
+
/**
* HTTP client connector using the Jetty project. Here is the list of parameters
* that are supported. They should be set in the Client's context before it is
@@ -201,22 +200,13 @@
*
* For the default SSL parameters see the Javadocs of the
* {@link DefaultSslContextFactory} class.
- *
+ *
* @author Jerome Louvel
* @author Tal Liron
*/
public class HttpClientHelper
extends org.restlet.engine.adapter.HttpClientHelper {
- public static void main(String[] args) throws Exception {
- Client client = new Client(Protocol.HTTP, Protocol.HTTPS);
- HttpClientHelper helper = new HttpClientHelper(client);
- helper.start();
- HttpClient httpClient = helper.getHttpClient();
- ContentResponse response = httpClient.GET("http://github.io/");
- response.getContentAsString();
- }
-
/**
* The wrapped Jetty HTTP client.
*/
@@ -239,7 +229,7 @@ public static void main(String[] args) throws Exception {
* Constructor. Properties can still be set before the wrapped Jetty HTTP
* client is effectively created and configured via the
* {@link #createHttpClient()} method.
- *
+ *
* @param client The client connector to help.
*/
public HttpClientHelper(Client client) {
@@ -247,14 +237,15 @@ public HttpClientHelper(Client client) {
getProtocols().add(Protocol.HTTP);
getProtocols().add(Protocol.HTTPS);
this.authenticationStore = null;
- this.cookieStore = isCookieSupported() ? new HttpCookieStore.Default()
+ this.cookieStore = isCookieSupported()
+ ? new HttpCookieStore.Default()
: new HttpCookieStore.Empty();
this.executor = null;
}
/**
* Creates a low-level HTTP client call from a high-level uniform call.
- *
+ *
* @param request The high-level request.
* @return A low-level HTTP client call.
*/
@@ -275,7 +266,7 @@ public ClientCall create(Request request) {
/**
* Creates a Jetty HTTP client.
- *
+ *
* @return A new HTTP client.
*/
protected HttpClient createHttpClient() {
@@ -290,54 +281,31 @@ protected HttpClient createHttpClient() {
}
HttpClientTransport httpTransport = null;
- HTTP2Client http2Client = null;
- HTTP3Client http3Client = null;
-
- switch (getHttpClientTransportMode()) {
- case "HTTP2":
- http2Client = new HTTP2Client();
- HttpClientTransportOverHTTP2 http2Transport = new HttpClientTransportOverHTTP2(
- http2Client);
- http2Transport.setUseALPN(true);
- httpTransport = http2Transport;
- break;
-
- case "HTTP3":
- ClientQuicConfiguration clientQuicConfig = new ClientQuicConfiguration(
- sslContextFactory, null);
- http3Client = new HTTP3Client(clientQuicConfig);
- http3Client.getQuicConfiguration()
- .setSessionRecvWindow(64 * 1024 * 1024);
- httpTransport = new HttpClientTransportOverHTTP3(http3Client);
- break;
-
- case "DYNAMIC":
- ClientConnectionFactory.Info http1 = HttpClientConnectionFactory.HTTP11;
-
- http2Client = new HTTP2Client();
- ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(
- http2Client);
-
- ClientQuicConfiguration quicConfiguration = new ClientQuicConfiguration(
- sslContextFactory, null);
- http3Client = new HTTP3Client(quicConfiguration);
- ClientConnectionFactoryOverHTTP3.HTTP3 http3 = new ClientConnectionFactoryOverHTTP3.HTTP3(
- http3Client);
-
- HttpClientTransportDynamic httpDynamicTransport = new HttpClientTransportDynamic(
- new ClientConnector(), http1, http2, http3);
- httpTransport = httpDynamicTransport;
- break;
-
- case "HTTP11":
- default:
- httpTransport = new HttpClientTransportOverHTTP();
- break;
+
+ final String httpClientTransportMode = getHttpClientTransportMode();
+ switch (httpClientTransportMode) {
+ case "HTTP2":
+ httpTransport = getHttpClientTransportForHttp2();
+ break;
+ case "HTTP3":
+ httpTransport = getHttpClientTransportForHttp3(sslContextFactory);
+ break;
+ case "DYNAMIC":
+ httpTransport = getHttpClientTransportForDynamicMode(sslContextFactory);
+ break;
+ case "HTTP11":
+ httpTransport = getHttpTransportForHttp1_1();
+ break;
+ default:
+ getLogger().log(Level.WARNING,
+ "Unknown HTTP client transport mode: {0}, use HTTP11 instead", httpClientTransportMode);
+ httpTransport = getHttpTransportForHttp1_1();
+ break;
}
HttpClient httpClient = new HttpClient(httpTransport);
httpClient.setAddressResolutionTimeout(getAddressResolutionTimeout());
- if(getAuthenticationStore() != null) {
+ if (getAuthenticationStore() != null) {
httpClient.setAuthenticationStore(getAuthenticationStore());
}
httpClient.setBindAddress(getBindAddress());
@@ -349,27 +317,25 @@ protected HttpClient createHttpClient() {
switch (getHttpComplianceMode()) {
- case "RFC7230":
- httpClient.setHttpCompliance(HttpCompliance.RFC7230);
- break;
- case "RFC7230_LEGACY":
- httpClient.setHttpCompliance(HttpCompliance.RFC7230_LEGACY);
- break;
- case "RFC2616":
- httpClient.setHttpCompliance(HttpCompliance.RFC2616);
- break;
- case "RFC2616_LEGACY":
- httpClient.setHttpCompliance(HttpCompliance.RFC2616_LEGACY);
- break;
+ case "RFC7230":
+ httpClient.setHttpCompliance(HttpCompliance.RFC7230);
+ break;
+ case "RFC7230_LEGACY":
+ httpClient.setHttpCompliance(HttpCompliance.RFC7230_LEGACY);
+ break;
+ case "RFC2616":
+ httpClient.setHttpCompliance(HttpCompliance.RFC2616);
+ break;
+ case "RFC2616_LEGACY":
+ httpClient.setHttpCompliance(HttpCompliance.RFC2616_LEGACY);
+ break;
}
httpClient.setHttpCookieStore(getCookieStore());
httpClient.setIdleTimeout(getIdleTimeout());
- httpClient.setMaxConnectionsPerDestination(
- getMaxConnectionsPerDestination());
+ httpClient.setMaxConnectionsPerDestination(getMaxConnectionsPerDestination());
httpClient.setMaxRedirects(getMaxRedirects());
- httpClient.setMaxRequestsQueuedPerDestination(
- getMaxRequestsQueuedPerDestination());
+ httpClient.setMaxRequestsQueuedPerDestination(getMaxRequestsQueuedPerDestination());
httpClient.setMaxResponseHeadersSize(getMaxResponseHeadersSize());
String httpProxyHost = getProxyHost();
@@ -393,10 +359,44 @@ protected HttpClient createHttpClient() {
return httpClient;
}
+ private static HttpClientTransportOverHTTP getHttpTransportForHttp1_1() {
+ return new HttpClientTransportOverHTTP();
+ }
+
+ private static HttpClientTransport getHttpClientTransportForHttp2() {
+ HTTP2Client http2Client = new HTTP2Client();
+ HttpClientTransportOverHTTP2 http2Transport = new HttpClientTransportOverHTTP2(http2Client);
+ http2Transport.setUseALPN(true);
+
+ return http2Transport;
+ }
+
+ private static HttpClientTransport getHttpClientTransportForHttp3(SslContextFactory.Client sslContextFactory) {
+ ClientQuicConfiguration quicConfiguration = new ClientQuicConfiguration(sslContextFactory, null);
+ HTTP3Client http3Client = new HTTP3Client(quicConfiguration);
+ http3Client.getQuicConfiguration().setSessionRecvWindow(64 * 1024 * 1024);
+
+ return new HttpClientTransportOverHTTP3(http3Client);
+ }
+
+ private static HttpClientTransport getHttpClientTransportForDynamicMode(SslContextFactory.Client sslContextFactory) {
+
+ ClientConnectionFactory.Info http1 = HttpClientConnectionFactory.HTTP11;
+
+ HTTP2Client http2Client = new HTTP2Client();
+ ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
+
+ ClientQuicConfiguration quicConfiguration = new ClientQuicConfiguration(sslContextFactory, null);
+ HTTP3Client http3Client = new HTTP3Client(quicConfiguration);
+ ClientConnectionFactoryOverHTTP3.HTTP3 http3 = new ClientConnectionFactoryOverHTTP3.HTTP3(http3Client);
+
+ return new HttpClientTransportDynamic(new ClientConnector(), http1, http2, http3);
+ }
+
/**
* The timeout in milliseconds for the DNS resolution of host addresses.
* Defaults to 15000.
- *
+ *
* @return The address resolution timeout.
*/
public long getAddressResolutionTimeout() {
@@ -406,16 +406,26 @@ public long getAddressResolutionTimeout() {
/**
* Returns the wrapped Jetty authentication store.
- *
+ *
* @return The wrapped Jetty authentication store.
*/
public AuthenticationStore getAuthenticationStore() {
return authenticationStore;
}
+ /**
+ * Sets the wrapped Jetty authentication store.
+ *
+ * @param authenticationStore The wrapped Jetty authentication store.
+ */
+ public void setAuthenticationStore(
+ AuthenticationStore authenticationStore) {
+ this.authenticationStore = authenticationStore;
+ }
+
/**
* The address to bind socket channels to. Default to null.
- *
+ *
* @return The bind address or null.
*/
public SocketAddress getBindAddress() {
@@ -432,7 +442,7 @@ public SocketAddress getBindAddress() {
/**
* The max time in milliseconds a connection can take to connect to
* destinations. Defaults to 15000.
- *
+ *
* @return The connect timeout.
*/
public long getConnectTimeout() {
@@ -442,17 +452,26 @@ public long getConnectTimeout() {
/**
* Returns the wrapped Jetty cookie store.
- *
+ *
* @return The wrapped Jetty cookie store.
*/
public HttpCookieStore getCookieStore() {
return this.cookieStore;
}
+ /**
+ * Sets the wrapped Jetty cookie store.
+ *
+ * @param cookieStore The wrapped Jetty cookie store.
+ */
+ public void setCookieStore(HttpCookieStore cookieStore) {
+ this.cookieStore = cookieStore;
+ }
+
/**
* The timeout in milliseconds for idle destinations to be removed. Defaults
* to 15000.
- *
+ *
* @return The address resolution timeout.
*/
public long getDestinationIdleTimeout() {
@@ -461,18 +480,27 @@ public long getDestinationIdleTimeout() {
}
/**
- * Returns the executor. By default returns an instance of
+ * Returns the executor. By default, returns an instance of
* {@link QueuedThreadPool}.
- *
+ *
* @return Returns the executor.
*/
public Executor getExecutor() {
return this.executor;
}
+ /**
+ * Sets the executor.
+ *
+ * @param executor The executor.
+ */
+ public void setExecutor(Executor executor) {
+ this.executor = executor;
+ }
+
/**
* Returns the wrapped Jetty HTTP client.
- *
+ *
* @return The wrapped Jetty HTTP client.
*/
public HttpClient getHttpClient() {
@@ -482,8 +510,8 @@ public HttpClient getHttpClient() {
/**
* Returns the HTTP compliance mode among the following options: "RFC7230",
* "RFC2616", "LEGACY", "RFC7230_LEGACY". See {@link HttpCompliance}.
- * Defaults to "RFC7230".
- *
+ * Default to "RFC7230".
+ *
* @return The HTTP compliance mode.
*/
public String getHttpComplianceMode() {
@@ -495,7 +523,7 @@ public String getHttpComplianceMode() {
* Returns the HTTP client transport mode among the following options:
* "HTTP11", "HTTP2", "HTTP3", "DYNAMIC. See {@link HttpClientTransport}.
* Defaults to "HTTP11".
- *
+ *
* @return The HTTP client transport mode.
*/
public String getHttpClientTransportMode() {
@@ -506,7 +534,7 @@ public String getHttpClientTransportMode() {
/**
* The max time in milliseconds a connection can be idle (that is, without
* traffic of bytes in either direction). Defaults to 60000.
- *
+ *
* @return The idle timeout.
*/
public long getIdleTimeout() {
@@ -524,7 +552,7 @@ public long getIdleTimeout() {
* load test), and it is recommended to set this value to a high value (at
* least as much as the threads present in the {@link #getExecutor()
* executor}).
- *
+ *
* @return The maximum connections per destination.
*/
public int getMaxConnectionsPerDestination() {
@@ -534,7 +562,7 @@ public int getMaxConnectionsPerDestination() {
/**
* The max number of HTTP redirects that are followed. Defaults to 8.
- *
+ *
* @return The maximum redirects.
*/
public int getMaxRedirects() {
@@ -553,7 +581,7 @@ public int getMaxRedirects() {
* If this client is used for load testing, it is common to have this
* parameter set to a high value, although this may impact latency (requests
* sit in the queue for a long time before being sent).
- *
+ *
* @return The maximum requests queues per destination.
*/
public int getMaxRequestsQueuedPerDestination() {
@@ -563,8 +591,8 @@ public int getMaxRequestsQueuedPerDestination() {
/**
* Returns the max size in bytes of the response headers. Default is -1
- * which is unlimited.
- *
+ * that is unlimited.
+ *
* @return the max size in bytes of the response headers.
*/
public int getMaxResponseHeadersSize() {
@@ -574,7 +602,7 @@ public int getMaxResponseHeadersSize() {
/**
* Returns the host name of the HTTP proxy, if specified.
- *
+ *
* @return the host name of the HTTP proxy, if specified.
*/
public String getProxyHost() {
@@ -584,7 +612,7 @@ public String getProxyHost() {
/**
* Returns the port of the HTTP proxy, if specified, 3128 otherwise.
- *
+ *
* @return the port of the HTTP proxy.
*/
public int getProxyPort() {
@@ -594,7 +622,7 @@ public int getProxyPort() {
/**
* The size in bytes of the buffer used to write requests. Defaults to 4096.
- *
+ *
* @return The request buffer size.
*/
public int getRequestBufferSize() {
@@ -605,7 +633,7 @@ public int getRequestBufferSize() {
/**
* The size in bytes of the buffer used to read responses. Defaults to
* 16384.
- *
+ *
* @return The response buffer size.
*/
public int getResponseBufferSize() {
@@ -616,7 +644,7 @@ public int getResponseBufferSize() {
/**
* The scheduler. Defaults to null. When null, creates a new instance of
* {@link ScheduledExecutorScheduler}.
- *
+ *
* @return The scheduler.
*/
public Scheduler getScheduler() {
@@ -625,8 +653,8 @@ public Scheduler getScheduler() {
/**
* The "User-Agent" HTTP header string. When null, uses the Jetty default.
- * Defaults to null.
- *
+ * Default to null.
+ *
* @return The user agent field or null.
*/
public String getUserAgentField() {
@@ -636,7 +664,7 @@ public String getUserAgentField() {
/**
* Indicates whether the connect operation is blocking. See
* {@link HttpClient#isConnectBlocking()}.
- *
+ *
* @return True if the connect operation is blocking.
*/
public boolean isConnectBlocking() {
@@ -647,7 +675,7 @@ public boolean isConnectBlocking() {
/**
* Whether to support cookies, storing and automatically sending them back.
* Defaults to false.
- *
+ *
* @return Whether to support cookies.
*/
public boolean isCookieSupported() {
@@ -657,7 +685,7 @@ public boolean isCookieSupported() {
/**
* Whether to follow HTTP redirects. Defaults to true.
- *
+ *
* @return Whether to follow redirects.
*/
public boolean isFollowRedirects() {
@@ -686,7 +714,7 @@ public boolean isFollowRedirects() {
* When not enforced, a "begin" event of a second request may happen before
* the "complete" event of a first request and allow for better usage of
* connections.
- *
+ *
* @return Whether request events must be strictly ordered.
*/
public boolean isStrictEventOrdering() {
@@ -694,34 +722,6 @@ public boolean isStrictEventOrdering() {
.getFirstValue("strictEventOrdering", "false"));
}
- /**
- * Sets the wrapped Jetty authentication store.
- *
- * @param authenticationStore The wrapped Jetty authentication store.
- */
- public void setAuthenticationStore(
- AuthenticationStore authenticationStore) {
- this.authenticationStore = authenticationStore;
- }
-
- /**
- * Sets the wrapped Jetty cookie store.
- *
- * @param cookieStore The wrapped Jetty cookie store.
- */
- public void setCookieStore(HttpCookieStore cookieStore) {
- this.cookieStore = cookieStore;
- }
-
- /**
- * Sets the executor.
- *
- * @param executor The executor.
- */
- public void setExecutor(Executor executor) {
- this.executor = executor;
- }
-
@Override
public void start() throws Exception {
super.start();
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpsServerHelper.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpsServerHelper.java
index e0f0416061..4e4fb78749 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpsServerHelper.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/HttpsServerHelper.java
@@ -66,16 +66,19 @@ public HttpsServerHelper(Server server) {
@Override
protected ConnectionFactory[] createConnectionFactories(
HttpConfiguration configuration) {
+ ConnectionFactory[] result;
+
try {
SslContextFactory.Server sslContextFactory = new RestletSslContextFactoryServer(
org.restlet.engine.ssl.SslUtils.getSslContextFactory(this));
- return AbstractConnectionFactory.getFactories(sslContextFactory,
+ result = AbstractConnectionFactory.getFactories(sslContextFactory,
new HttpConnectionFactory(configuration));
} catch (Exception e) {
+ result = null;
getLogger().log(Level.WARNING,
"Unable to create the Jetty SSL context factory", e);
}
- return null;
+ return result;
}
}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/JettyServerHelper.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/JettyServerHelper.java
index dcacf062e8..d1feffb732 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/JettyServerHelper.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/JettyServerHelper.java
@@ -9,19 +9,10 @@
package org.restlet.ext.jetty;
-import java.net.Socket;
-import java.util.Arrays;
-import java.util.concurrent.Executor;
-
import org.eclipse.jetty.io.ArrayByteBufferPool;
import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.LowResourceMonitor;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Response;
-import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.*;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
@@ -30,6 +21,10 @@
import org.restlet.Server;
import org.restlet.ext.jetty.internal.JettyServerCall;
+import java.net.Socket;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
/**
* Abstract Jetty web server connector. Here is the list of parameters that are
* supported. They should be set in the Server's context before it is started:
@@ -77,15 +72,14 @@
*
connector.acceptors |
* int |
* -1 |
- * Connector acceptor thread count; when -1, Jetty will default to
- * {@link Runtime#availableProcessors()} / 2, with a minimum of 1 |
+ * Connector acceptor thread count; when -1, Jetty will default to 1 |
*
*
* | connector.selectors |
* int |
* -1 |
- * Connector selector thread count; when -1, Jetty will default to
- * {@link Runtime#availableProcessors()} |
+ * Connector selector thread count; When less or equal than 0,
+ * Jetty computes a default value derived from a heuristic over available CPUs and thread pool size.} |
*
*
* | connector.acceptQueueSize |
@@ -152,41 +146,66 @@
* lowResource.period |
* int |
* 1000 |
- * Low resource monitor period in milliseconds; when 0, low resource
+ * | Low-resource monitor period in milliseconds; when 0, low-resource
* monitoring is disabled |
*
*
* | lowResource.threads |
* boolean |
* true |
- * Low resource monitor, whether to check if we're low on threads |
+ * Low-resource monitor, whether to check if we're low on threads |
*
*
* | lowResource.maxMemory |
* int |
* 0 |
- * Low resource monitor max memory in bytes; when 0, the check disabled;
+ * | Low-resource monitor max memory in bytes; when 0, the check disabled;
* memory used is calculated as (totalMemory-freeMemory) |
*
*
* | lowResource.maxConnections |
* int |
* 0 |
- * Low resource monitor max connections; when 0, the check is disabled |
+ * Low-resource monitor max connections; when 0, the check is disabled. Deprecated, see server.maxConnections |
*
*
* | lowResource.idleTimeout |
* int |
* 1000 |
- * Low resource monitor idle timeout in milliseconds; applied to EndPoints
- * when in the low resources state |
+ * Low-resource monitor idle timeout in milliseconds; applied to EndPoints
+ * when in the low-resources state |
*
*
* | lowResource.stopTimeout |
* long |
* 30000 |
- * Low resource monitor stop timeout in milliseconds; the maximum time
- * allowed for the service to shutdown |
+ * Low-resource monitor stop timeout in milliseconds; the maximum time
+ * allowed for the service to shutdown. Deprecated, use shutdown.timeout instead |
+ *
+ *
+ * | server.maxConnections |
+ * int |
+ * 0 |
+ * Server max connections; when 0, there is no limit |
+ *
+ *
+ * | server.maxConnections.idleTimeout |
+ * long |
+ * 0 |
+ * The endpoint idle timeout in milliseconds to apply when the connection limit is reached; when 0, there is no idle timeout. |
+ *
+ *
+ * | shutdown.gracefully |
+ * boolean |
+ * true |
+ * When true, upon JVM shutdown, the Jetty server will block incoming requests and wait for pending
+ * requests to end before shutting down. Otherwise, incoming requests are not blocked and all requests are aborted. |
+ *
+ *
+ * | shutdown.timeout |
+ * long |
+ * 30000 |
+ * Server shutdown timeout in milliseconds. Defaults to 30000. |
*
*
*
@@ -199,45 +218,6 @@
public abstract class JettyServerHelper
extends org.restlet.engine.adapter.HttpServerHelper {
- /**
- * Jetty server wrapped by a parent Restlet HTTP server connector.
- *
- * @author Jerome Louvel
- * @author Tal Liron
- */
- private static class WrappedServer extends org.eclipse.jetty.server.Server {
- private final JettyServerHelper serverHelper;
-
- /**
- * Constructor.
- *
- * @param serverHelper The Jetty HTTP server.
- * @param threadPool The thread pool.
- */
- public WrappedServer(JettyServerHelper serverHelper,
- ThreadPool threadPool) {
- super(threadPool);
- this.serverHelper = serverHelper;
- }
-
- /**
- * Handler method converting a Jetty call into a Restlet Call.
- *
- * @param request The Jetty request to handle.
- * @param response The Jetty response to handle.
- * @param callback The Jetty callback to use if needed.
- * @return True if processing was successful.
- */
- @Override
- public boolean handle(Request request, Response response,
- Callback callback) throws Exception {
- this.serverHelper
- .handle(new JettyServerCall(this.serverHelper.getHelped(),
- request, response, callback));
- return true;
- }
- }
-
/** The wrapped Jetty server. */
private volatile org.eclipse.jetty.server.Server wrappedServer;
@@ -286,6 +266,7 @@ protected abstract ConnectionFactory[] createConnectionFactories(
*/
private Connector createConnector(org.eclipse.jetty.server.Server server) {
final HttpConfiguration configuration = createConfiguration();
+
final ConnectionFactory[] connectionFactories = createConnectionFactories(
configuration);
@@ -300,44 +281,42 @@ private Connector createConnector(org.eclipse.jetty.server.Server server) {
connectionFactories);
final String address = getHelped().getAddress();
- if (address != null)
+ if (address != null) {
connector.setHost(address);
+ }
connector.setPort(getHelped().getPort());
connector.setAcceptQueueSize(getConnectorAcceptQueueSize());
connector.setIdleTimeout(getConnectorIdleTimeout());
-// connector.setSoLingerTime(getConnectorSoLingerTime());
-// connector.setStopTimeout(getConnectorStopTimeout());
+ connector.setShutdownIdleTimeout(getShutdownTimeout());
+ // connector.setSoLingerTime(getConnectorSoLingerTime());
return connector;
}
/**
- * Creates a Jetty low resource monitor.
+ * Creates a Jetty low-resource monitor.
*
* @param server A Jetty server.
- * @return A Jetty low resource monitor or null.
+ * @return A Jetty low-resource monitor or null.
*/
private LowResourceMonitor createLowResourceMonitor(
org.eclipse.jetty.server.Server server) {
+ final LowResourceMonitor result;
+
final int period = getLowResourceMonitorPeriod();
if (period > 0) {
- final LowResourceMonitor lowResourceMonitor = new LowResourceMonitor(
- server);
- lowResourceMonitor.setMonitoredConnectors(
- Arrays.asList(server.getConnectors()));
- lowResourceMonitor.setPeriod(period);
- lowResourceMonitor
- .setMonitorThreads(getLowResourceMonitorThreads());
- lowResourceMonitor.setMaxMemory(getLowResourceMonitorMaxMemory());
-// lowResourceMonitor.setMaxConnections(getLowResourceMonitorMaxConnections());
- lowResourceMonitor.setLowResourcesIdleTimeout(
- getLowResourceMonitorIdleTimeout());
-// lowResourceMonitor.setStopTimeout(getLowResourceMonitorStopTimeout());
- server.addBean(lowResourceMonitor);
- return lowResourceMonitor;
+ result = new LowResourceMonitor(server);
+ result.setMonitoredConnectors(Arrays.asList(server.getConnectors()));
+ result.setPeriod(period);
+ result.setMonitorThreads(getLowResourceMonitorThreads());
+ result.setMaxMemory(getLowResourceMonitorMaxMemory());
+ result.setLowResourcesIdleTimeout(getLowResourceMonitorIdleTimeout());
+ } else {
+ result = null;
}
- return null;
+
+ return result;
}
/**
@@ -350,18 +329,64 @@ private org.eclipse.jetty.server.Server createServer() {
final ThreadPool threadPool = createThreadPool();
// Server
- final org.eclipse.jetty.server.Server server = new WrappedServer(this,
- threadPool);
+ final org.eclipse.jetty.server.Server jettyServer = new org.eclipse.jetty.server.Server(threadPool);
- // Connector
- final Connector connector = createConnector(server);
- server.addConnector(connector);
+ int serverMaxConnections = getServerMaxConnections();
+ if (serverMaxConnections > 0) {
+ ConnectionLimit connectionLimit = new ConnectionLimit(serverMaxConnections, jettyServer);
+ connectionLimit.setIdleTimeout(getServerMaxConnectionsIdleTimeout());
+ jettyServer.addBean(connectionLimit);
+ }
+
+ jettyServer.setStopAtShutdown(getShutdownGracefully());
+ if (getShutdownGracefully()) {
+ jettyServer.setStopTimeout(getShutdownTimeout());
+ } else {
+ jettyServer.setStopTimeout(0);
+ }
- // Low resource monitor (must be created after connectors have been
- // added)
- createLowResourceMonitor(server);
+ jettyServer.setHandler(createJettyHandler());
- return server;
+ // Connector
+ final Connector connector = createConnector(jettyServer);
+ jettyServer.addConnector(connector);
+
+ // Low-resource monitor (must be created after connectors have been added)
+ LowResourceMonitor lowResourceMonitor = createLowResourceMonitor(jettyServer);
+ jettyServer.addBean(lowResourceMonitor);
+
+ return jettyServer;
+ }
+
+ /**
+ * Creates the Jetty handler that wraps the {@link JettyServerHelper}.
+ *
+ * @return the Jetty handler that wraps the {@link JettyServerHelper}.
+ */
+ private Handler.Abstract createJettyHandler() {
+ final Handler.Abstract result;
+
+ final JettyServerHelper jettyServerHelper = this;
+ Handler.Abstract jettyServerHelperWrapperHandler = new Handler.Abstract() {
+ @Override
+ public boolean handle(Request request, Response response, Callback callback) {
+ JettyServerCall httpCall = new JettyServerCall(jettyServerHelper.getHelped(),
+ request, response, callback);
+ jettyServerHelper.handle(httpCall);
+ return true; // Indicates that the request is accepted
+ };
+ };
+
+ if (getShutdownGracefully()) {
+ // StatisticsHandler for graceful shutdown
+ final StatisticsHandler statisticsHandler = new StatisticsHandler();
+ statisticsHandler.setHandler(jettyServerHelperWrapperHandler);
+ result = statisticsHandler;
+ } else {
+ result = jettyServerHelperWrapperHandler;
+ }
+
+ return result;
}
/**
@@ -375,14 +400,18 @@ private ThreadPool createThreadPool() {
threadPool.setMaxThreads(getThreadPoolMaxThreads());
threadPool.setThreadsPriority(getThreadPoolThreadsPriority());
threadPool.setIdleTimeout(getThreadPoolIdleTimeout());
- threadPool.setStopTimeout(getThreadPoolStopTimeout());
+ if (getShutdownGracefully()) {
+ threadPool.setStopTimeout(getThreadPoolStopTimeout());
+ } else {
+ threadPool.setStopTimeout(0); // The thread pool stops immediately.
+ }
+
return threadPool;
}
/**
* Connector acceptor thread count. Defaults to -1. When -1, Jetty will
- * default to {@link Runtime#availableProcessors()} / 2, with a minimum of
- * 1.
+ * default to 1.
*
* @return Connector acceptor thread count.
*/
@@ -392,9 +421,10 @@ public int getConnectorAcceptors() {
}
/**
- * Connector accept queue size. Defaults to 0.
+ * Connector "accept" queue size.
+ * Defaults to 0.
*
- * Also known as accept backlog.
+ * Also known as "accept" backlog.
*
* @return Connector accept queue size.
*/
@@ -450,8 +480,10 @@ public Scheduler getConnectorScheduler() {
}
/**
- * Connector selector thread count. Defaults to -1. When 0, Jetty will
- * default to {@link Runtime#availableProcessors()}.
+ * Connector selector thread count.
+ * Defaults to -1.
+ * When less or equal than 0,
+ * Jetty computes a default value derived from a heuristic over available CPUs and thread pool size.
*
* @return Connector acceptor thread count.
*/
@@ -476,10 +508,12 @@ public int getConnectorSoLingerTime() {
/**
* Connector stop timeout in milliseconds. Defaults to 30000.
*
- * The maximum time allowed for the service to shutdown.
+ * The maximum time allowed for the service to shut down.
*
* @return Connector stop timeout.
+ * @deprecated cf {@link #getShutdownTimeout()}
*/
+ @Deprecated
public int getConnectorStopTimeout() {
return Integer.parseInt(getHelpedParameters()
.getFirstValue("connector.stopTimeout", "30000"));
@@ -492,14 +526,14 @@ public int getConnectorStopTimeout() {
*/
public int getHttpHeaderCacheSize() {
return Integer.parseInt(getHelpedParameters()
- .getFirstValue("http.headerCacheSize", "512"));
+ .getFirstValue("http.headerCacheSize", "1024"));
}
/**
* HTTP output buffer size in bytes. Defaults to 32*1024.
*
* A larger buffer can improve performance by allowing a content producer to
- * run without blocking, however larger buffers consume more memory and may
+ * run without blocking, however, larger buffers consume more memory and may
* induce some latency before a client starts processing the content.
*
* @return HTTP output buffer size.
@@ -527,7 +561,7 @@ public int getHttpRequestHeaderSize() {
* HTTP response header size in bytes. Defaults to 8*1024.
*
* Larger headers will allow for more and/or larger cookies and longer HTTP
- * headers (e.g. for redirection). However, larger headers will also consume
+ * headers (e.g., for redirection). However, larger headers will also consume
* more memory.
*
* @return HTTP response header size.
@@ -538,11 +572,12 @@ public int getHttpResponseHeaderSize() {
}
/**
- * Low resource monitor idle timeout in milliseconds. Defaults to 1000.
+ * Low-resource monitor idle timeout in milliseconds.
+ * Defaults to 1000.
*
- * Applied to EndPoints when in the low resources state.
+ * Applied to EndPoints when in the low-resources state.
*
- * @return Low resource monitor idle timeout.
+ * @return Low-resource monitor idle timeout.
*/
public int getLowResourceMonitorIdleTimeout() {
return Integer.parseInt(getHelpedParameters()
@@ -550,23 +585,27 @@ public int getLowResourceMonitorIdleTimeout() {
}
/**
- * Low resource monitor max connections. Defaults to 0. When 0, the check is
- * disabled.
+ * Low-resource monitor max connections.
+ * Defaults to 0.
+ * When 0, the check is disabled.
*
- * @return Low resource monitor max connections.
+ * @return Low-resource monitor max connections.
+ * @deprecated cf {@link #getServerMaxConnections()}
*/
+ @Deprecated
public int getLowResourceMonitorMaxConnections() {
return Integer.parseInt(getHelpedParameters()
.getFirstValue("lowResource.maxConnections", "0"));
}
/**
- * Low resource monitor max memory in bytes. Defaults to 0. When 0, the
- * check disabled.
+ * Low-resource monitor max memory in bytes.
+ * Defaults to 0.
+ * When 0, the check is disabled.
*
* Memory used is calculated as (totalMemory-freeMemory).
*
- * @return Low resource monitor max memory.
+ * @return Low-resource monitor max memory.
*/
public long getLowResourceMonitorMaxMemory() {
return Long.parseLong(getHelpedParameters()
@@ -574,10 +613,11 @@ public long getLowResourceMonitorMaxMemory() {
}
/**
- * Low resource monitor period in milliseconds. Defaults to 1000. When 0,
- * low resource monitoring is disabled.
+ * Low-resource monitor period in milliseconds.
+ * Defaults to 1000.
+ * When 0, low-resource monitoring is disabled.
*
- * @return Low resource monitor period.
+ * @return Low-resource monitor period.
*/
public int getLowResourceMonitorPeriod() {
return Integer.parseInt(getHelpedParameters()
@@ -585,28 +625,99 @@ public int getLowResourceMonitorPeriod() {
}
/**
- * Low resource monitor stop timeout in milliseconds. Defaults to 30000.
+ * Low-resource monitor stop timeout in milliseconds.
+ * Defaults to 30000.
*
- * The maximum time allowed for the service to shutdown.
+ * The maximum time allowed for the service to shut down.
*
- * @return Low resource monitor stop timeout.
+ * @return Low-resource monitor stop timeout.
+ * @deprecated cf {@link #getShutdownTimeout()}
*/
+ @Deprecated
public long getLowResourceMonitorStopTimeout() {
return Long.parseLong(getHelpedParameters()
.getFirstValue("lowResource.stopTimeout", "30000"));
}
/**
- * Low resource monitor, whether to check if we're low on threads. Defaults
+ * Low-resource monitor, whether to check if we're low on threads. Defaults
* to true.
*
- * @return Low resource monitor threads.
+ * @return Low-resource monitor threads.
*/
public boolean getLowResourceMonitorThreads() {
return Boolean.parseBoolean(getHelpedParameters()
.getFirstValue("lowResource.threads", "true"));
}
+ /**
+ * Server max connections.
+ * Defaults to 0.
+ * When 0, the check is disabled.
+ *
+ * @return Low-resource monitor max connections.
+ */
+ public int getServerMaxConnections() {
+ final int result;
+
+ final String serverMaxConnectionsValue = getHelpedParameters().getFirstValue("server.maxConnections");
+
+ if (serverMaxConnectionsValue == null) {
+ result = getLowResourceMonitorMaxConnections();
+ } else {
+ result = Integer.parseInt(serverMaxConnectionsValue);
+ }
+
+ return result;
+ }
+
+ /**
+ * The endpoint idle timeout in milliseconds to apply when the connection limit is reached.
+ * Defaults to 0.
+ * When 0, there is no idle timeout.
+ *
+ * The maximum time allowed for the endpoint to close when the connection limit is reached.
+ *
+ * @return The endpoint idle timeout in milliseconds to apply when the connection limit is reached.
+ */
+ public long getServerMaxConnectionsIdleTimeout() {
+ return Long.parseLong(getHelpedParameters()
+ .getFirstValue("server.maxConnections.idleTimeout", "0"));
+ }
+
+ /**
+ * When true, upon JVM shutdown, the Jetty server will block incoming requests and wait for pending
+ * requests to end before shutting down.
+ * Otherwise, incoming requests are not blocked and all requests are aborted.
+ * Defaults to true.
+ * @return True if upon JVM shutdown, the Jetty server will block incoming requests and wait for pending requests to
+ * end before shutting down.
+ * Otherwise, incoming requests are not blocked and all requests are aborted.
+ */
+ public boolean getShutdownGracefully() {
+ return Boolean.parseBoolean(getHelpedParameters().getFirstValue(
+ "shutdown.gracefully", "true"));
+ }
+
+ /**
+ * Server shutdown timeout in milliseconds. Defaults to 30000.
+ *
+ * @return Server shutdown timeout.
+ */
+ public long getShutdownTimeout() {
+ final long result;
+
+ final String shutdownTimeoutValue = getHelpedParameters().getFirstValue("shutdown.timeout");
+
+ if (shutdownTimeoutValue == null) {
+ result = getLowResourceMonitorStopTimeout();
+ } else {
+ result = Long.parseLong(shutdownTimeoutValue);
+ }
+
+ return result;
+ }
+
/**
* Thread pool idle timeout in milliseconds. Defaults to 60000.
*
@@ -642,7 +753,7 @@ public int getThreadPoolMinThreads() {
/**
* Thread pool stop timeout in milliseconds. Defaults to 5000.
*
- * The maximum time allowed for the service to shutdown.
+ * The maximum time allowed for the service to shut down.
*
* @return Thread pool stop timeout.
*/
@@ -668,8 +779,9 @@ public int getThreadPoolThreadsPriority() {
* @return The wrapped Jetty server.
*/
protected org.eclipse.jetty.server.Server getWrappedServer() {
- if (this.wrappedServer == null)
+ if (this.wrappedServer == null) {
this.wrappedServer = createServer();
+ }
return this.wrappedServer;
}
@@ -678,8 +790,7 @@ protected org.eclipse.jetty.server.Server getWrappedServer() {
*
* @param wrappedServer The wrapped Jetty server.
*/
- protected void setWrappedServer(
- org.eclipse.jetty.server.Server wrappedServer) {
+ protected void setWrappedServer(org.eclipse.jetty.server.Server wrappedServer) {
this.wrappedServer = wrappedServer;
}
@@ -692,15 +803,15 @@ public void start() throws Exception {
+ " server on port " + getHelped().getPort());
try {
server.start();
+ // We won't know the local port until after the server starts
+ setEphemeralPort(connector.getLocalPort());
} catch (Exception e) {
- // Make sure that all resources are released, otherwise threadpool
+ // Make sure that all resources are released, otherwise thread-pool
// may still be running.
server.stop();
throw e;
}
- // We won't know the local port until after the server starts
- setEphemeralPort(connector.getLocalPort());
}
@Override
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyHandler.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyHandler.java
index 5e41ad3382..ecbe2033d0 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyHandler.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyHandler.java
@@ -20,7 +20,7 @@
/**
* Jetty handler that knows how to convert Jetty calls into Restlet calls. This
- * handler isn't a full server, if you use it you need to manually setup the
+ * handler isn't a full server, if you use it, you need to manually set up the
* Jetty server connector and add this handler to a Jetty server.
*
* @author Valdis Rigdon
@@ -48,10 +48,11 @@ public JettyHandler(Server server) {
* @param secure Indicates if the server supports HTTP or HTTPS.
*/
public JettyHandler(Server server, boolean secure) {
- if (secure)
+ if (secure) {
this.helper = new HttpsServerHelper(server);
- else
+ } else {
this.helper = new HttpServerHelper(server);
+ }
}
@Override
@@ -77,8 +78,9 @@ protected void doStop() throws Exception {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
- this.helper.handle(new JettyServerCall(this.helper.getHelped(), request,
- response, callback));
+ JettyServerCall httpCall = new JettyServerCall(this.helper.getHelped(),
+ request, response, callback);
+ this.helper.handle(httpCall);
return true;
}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyServerCall.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyServerCall.java
index d95e9bc465..8d81593a42 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyServerCall.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/JettyServerCall.java
@@ -9,13 +9,6 @@
package org.restlet.ext.jetty.internal;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.cert.Certificate;
-import java.util.Arrays;
-import java.util.List;
-
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
@@ -29,6 +22,13 @@
import org.restlet.engine.adapter.ServerCall;
import org.restlet.util.Series;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Call that is used by the Jetty HTTP server connectors.
*
@@ -93,12 +93,20 @@ public Callback getCallback() {
@Override
public List getCertificates() {
+ final List result;
+
if (getEndPoint() instanceof SslEndPoint sslEndPoint) {
- return Arrays.asList(sslEndPoint.getSslSessionData()
- .peerCertificates());
- } else {
- return null;
+ if (sslEndPoint.getSslSessionData() != null
+ && sslEndPoint.getSslSessionData().peerCertificates() != null) {
+ result = Arrays.asList(sslEndPoint.getSslSessionData().peerCertificates());
+ } else {
+ result = null;
+ }
+ } else {
+ result = null;
}
+
+ return result;
}
/**
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryClient.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryClient.java
index 6b940e8a00..0aa811c5aa 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryClient.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryClient.java
@@ -52,11 +52,10 @@ public SSLEngine newSSLEngine(String host, int port) {
@Override
public SSLServerSocket newSslServerSocket(String host, int port, int backlog)
throws IOException {
- SSLServerSocketFactory factory = getSslContext()
- .getServerSocketFactory();
- return (SSLServerSocket) ((host == null) ? factory.createServerSocket(
- port, backlog) : factory.createServerSocket(port, backlog,
- InetAddress.getByName(host)));
+ SSLServerSocketFactory factory = getSslContext().getServerSocketFactory();
+ return (SSLServerSocket) ((host == null)
+ ? factory.createServerSocket(port, backlog) :
+ factory.createServerSocket(port, backlog, InetAddress.getByName(host)));
}
@Override
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryServer.java b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryServer.java
index 5a70a8e46d..589a41846b 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryServer.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/main/java/org/restlet/ext/jetty/internal/RestletSslContextFactoryServer.java
@@ -52,11 +52,10 @@ public SSLEngine newSSLEngine(String host, int port) {
@Override
public SSLServerSocket newSslServerSocket(String host, int port, int backlog)
throws IOException {
- SSLServerSocketFactory factory = getSslContext()
- .getServerSocketFactory();
- return (SSLServerSocket) ((host == null) ? factory.createServerSocket(
- port, backlog) : factory.createServerSocket(port, backlog,
- InetAddress.getByName(host)));
+ SSLServerSocketFactory factory = getSslContext().getServerSocketFactory();
+ return (SSLServerSocket) ((host == null)
+ ? factory.createServerSocket(port, backlog)
+ : factory.createServerSocket(port, backlog, InetAddress.getByName(host)));
}
@Override
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/Lock.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/Lock.java
new file mode 100644
index 0000000000..ec273f2de0
--- /dev/null
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/Lock.java
@@ -0,0 +1,38 @@
+package org.restlet.ext.jetty;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+import java.util.logging.Logger;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class Lock {
+ private static final Logger LOGGER = Logger.getLogger("Lock");
+
+ private final CountDownLatch lock = new CountDownLatch(1);
+ private final String name;
+
+ public Lock(String name) {
+ this.name = name;
+ }
+
+ public void unlock() {
+ log("lock " + name + " unlock");
+ lock.countDown();
+ }
+
+ public boolean awaitForUnlockingFor(final Duration waitTime) throws InterruptedException {
+ log("lock " + name + " awaitForUnlocking");
+
+ final long waitTimeInMs = waitTime.toMillis();
+ boolean await = lock.await(waitTimeInMs, MILLISECONDS);
+ log("lock " + name + " awaitForUnlocking done " + await);
+ return await;
+ }
+
+ private static void log(final String message) {
+ LOGGER.fine(Instant.now().toString() + " " + message);
+ }
+
+}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/ShutdownHookTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/ShutdownHookTestCase.java
new file mode 100644
index 0000000000..85c13e4171
--- /dev/null
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/ShutdownHookTestCase.java
@@ -0,0 +1,367 @@
+/**
+ * Copyright 2005-2014 Restlet
+ *
+ * The contents of this file are subject to the terms of one of the following
+ * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
+ * select the license that you prefer but you may not use this file except in
+ * compliance with one of these Licenses.
+ *
+ * You can obtain a copy of the Apache 2.0 license at
+ * http://www.opensource.org/licenses/apache-2.0
+ *
+ * You can obtain a copy of the EPL 1.0 license at
+ * http://www.opensource.org/licenses/eclipse-1.0
+ *
+ * See the Licenses for the specific language governing permissions and
+ * limitations under the Licenses.
+ *
+ * Alternatively, you can obtain a royalty free commercial license with less
+ * limitations, transferable or non-transferable, directly at
+ * http://restlet.com/products/restlet-framework
+ *
+ * Restlet is a registered trademark of Restlet S.A.S.
+ */
+
+package org.restlet.ext.jetty;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.restlet.*;
+import org.restlet.data.MediaType;
+import org.restlet.data.Protocol;
+import org.restlet.data.Status;
+import org.restlet.engine.Engine;
+import org.restlet.resource.ClientResource;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ShutdownHookTestCase {
+ private static final Logger LOGGER = Logger.getLogger("ShutdownHookTest");
+ private static boolean shouldDebug = false;
+
+ @BeforeAll
+ private static void setUp() {
+ LOGGER.setLevel(Level.INFO);
+ }
+
+ /**
+ * Validates that the server stops immediately when no requests are currently handled.
+ */
+ @Test
+ public void whenServerIsNotHandlingRequestThenItStopsImmediately() throws Exception {
+ // Given a server resource that takes 1 min to send a response
+ final Restlet hangingRestlet = newHangingRestlet(Duration.ofMinutes(1));
+ // Given a server with a 3-seconds graceful shutdown
+ final Server server = startServerWithGracefulShutdown(Duration.ofSeconds(3), hangingRestlet);
+
+ // When the server stops
+ final Instant serverAskedToStopInstant = stopServer(server);
+
+ // Then the server stops immediately (no pending request)
+ assertIntervalBetweenDatesEquals(Duration.ZERO, serverAskedToStopInstant, Instant.now());
+ }
+
+ /**
+ * Validates that hanging requests are aborted immediately when graceful shutdown is OFF.
+ *
+ * This is done by making a request froze, then stopping the server, and checking that it didn't wait.
+ */
+ @Test
+ public void whenServerIsHandlingBlockingRequestThenItStopsImmediately() throws Exception {
+ // Given a server resource that takes 1 min to send a response
+ final Lock lock = new Lock("Server");
+ final Restlet hangingRestlet = newHangingAndLockedRestlet(Duration.ofMinutes(1), lock);
+ // Given a server without graceful shutdown
+ Server server = startServerWithoutGracefulShutdown(hangingRestlet);
+
+ // Given a client that sends a request
+ final TestClient testClient = new TestClient(server);
+ new Thread(testClient).start();
+
+ // When we stop the server while there is a pending request
+ log("before resource unlock");
+ final boolean isResourceUnlocked = lock.awaitForUnlockingFor(Duration.ofSeconds(3));
+ log("after resource unlock");
+ log("before stopping server while request is pending");
+ final Instant serverAskedToStopInstant = stopServer(server);
+ log("before client unlock");
+ final boolean isClientResourceUnlocked = testClient.lock.awaitForUnlockingFor(Duration.ofSeconds(4));
+ log("after client unlock");
+
+ // Then
+ assertTrue(isResourceUnlocked, "The resource didn't receive the request");
+ assertTrue(isClientResourceUnlocked, "The client didn't achieve the request");
+ assertTrue(testClient.cr.getStatus().isError(), "The request should have ended in error");
+ assertIntervalBetweenDatesEquals(Duration.ZERO, serverAskedToStopInstant, testClient.stoppedAt);
+ assertIntervalBetweenDatesEquals(Duration.ZERO, serverAskedToStopInstant, Instant.now());
+ }
+
+ /**
+ * Validates that hanging requests are aborted after the server has waited for the timeout when graceful shutdown is ON.
+ *
+ * This is done by making a request froze, then stopping the server, and checking that it waited the expected amount of time before shutting down.
+ */
+ @Test
+ public void whenServerIsHandlingBlockingRequestThenItGracefullyWaitsFor1SecondBeforeStopping() throws Exception {
+ // Given a server resource that takes 1 min to send a response
+ final Lock serverLock = new Lock("Server");
+ final Restlet hangingRestlet = newHangingAndLockedRestlet(Duration.ofMinutes(1), serverLock);
+
+ // Given a server with a 1-second graceful shutdown
+ final Duration shutdownTimeout = Duration.ofSeconds(1);
+ final Server server = startServerWithGracefulShutdown(shutdownTimeout, hangingRestlet);
+
+ // Given a client that sends a request
+ final TestClient hangingClient = new TestClient(server);
+ new Thread(hangingClient).start();
+
+ // When we stop the server while there is a pending request
+ final boolean isResourceUnlocked = serverLock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2));
+ log("Before ask server to stop");
+ final Instant serverAskedToStopInstant = stopServer(server);
+ log("After ask server to stop");
+ final boolean isClientResourceUnlocked = hangingClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2));
+
+ // Then
+ assertTrue(isResourceUnlocked, "The resource didn't receive the request");
+ assertTrue(isClientResourceUnlocked, "The client didn't achieved the request");
+ assertTrue(hangingClient.cr.getStatus().isError(), "The request should have ended in error");
+
+ assertIntervalBetweenDatesEquals(shutdownTimeout, serverAskedToStopInstant, hangingClient.stoppedAt);
+ assertIntervalBetweenDatesEquals(toJettyEffectiveTimeout(shutdownTimeout), serverAskedToStopInstant, Instant.now());
+ }
+
+ /**
+ * Validates that incoming requests are refused after the server is stopping when graceful shutdown is ON.
+ *
+ * This is done by making a request froze, then stopping the server, and checking that a new request is not taken into account.
+ */
+ @Test
+ public void whenServerIsHandlingBlockingRequestThenItRefusesNewRequest() throws Exception {
+ // Given a server resource that takes 1 min to send a response
+ final Lock lock = new Lock("Server");
+ final Restlet hangingRestlet = newHangingAndLockedRestlet(Duration.ofMinutes(1), lock);
+
+ // Given a server with a 1-second graceful shutdown
+ final Duration shutdownTimeout = Duration.ofSeconds(1);
+ final Server server = startServerWithGracefulShutdown(shutdownTimeout, hangingRestlet);
+
+ // Given a client that sends a request
+ final TestClient firstTestClient = new TestClient(server);
+ new Thread(firstTestClient).start();
+
+ // When
+ final boolean isResourceUnlocked = lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2));
+ final Instant serverAskedToStopInstant = stopServer(server);
+ final TestClient blockedTestClient = new TestClient(server);
+ blockedTestClient.run();
+ final boolean isFirstClientResourceUnlocked = firstTestClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2));
+ final boolean isBlockedClientResourceUnlocked = blockedTestClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2));
+
+ // Then
+ assertTrue(isResourceUnlocked, "The resource didn't receive the request");
+ assertTrue(isFirstClientResourceUnlocked, "The first client didn't achieved the request");
+ assertTrue(isBlockedClientResourceUnlocked, "The \"blocked\" client didn't achieved the request");
+ assertTrue(firstTestClient.cr.getStatus().isError(), "The request should have ended in error");
+ assertIntervalBetweenDatesEquals(shutdownTimeout, serverAskedToStopInstant, firstTestClient.stoppedAt);
+ assertTrue(blockedTestClient.cr.getStatus().isConnectorError(), "Any new client is blocked and fails with a connection error");
+ assertIntervalBetweenDatesEquals(toJettyEffectiveTimeout(shutdownTimeout), serverAskedToStopInstant, Instant.now());
+ }
+
+ /**
+ * Validates that hanging requests for a short amount of time are handled before the server has waited for the timeout when graceful shutdown is ON.
+ *
+ * This is done by making a request froze for a short amount of time, then stopping the server, and checking that the request has been handled.
+ */
+ @Test
+ public void whenServerIsHandlingLongRequestThenRequestIsHandledCorrectlyBeforeStopping() throws Exception {
+ // Given a server resource that takes 1 sec to send a response
+ final Lock lock = new Lock("Server");
+ Duration requestHangingTime = Duration.ofSeconds(1);
+ final Restlet hangingRestlet = newHangingAndLockedRestlet(requestHangingTime, lock);
+
+ // Given a server with a 20-seconds graceful shutdown
+ final Duration shutdownTimeout = Duration.ofSeconds(20);
+ final Server server = startServerWithGracefulShutdown(shutdownTimeout, hangingRestlet);
+
+ // Given a client that sends a request
+ final TestClient testClient = new TestClient(server);
+ new Thread(testClient).start();
+
+ // When
+ final boolean isResourceUnlocked = lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2));
+ final Instant serverAskedToStopInstant = stopServer(server);
+ log("Client resource wait lock");
+ final boolean isClientResourceUnlocked = testClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2));
+ log("Client resource unlocked");
+
+ // Then
+ assertTrue(isResourceUnlocked, "The resource didn't receive the request");
+ assertTrue(isClientResourceUnlocked, "The client didn't achieve the request");
+ assertEquals(Status.SUCCESS_OK, testClient.cr.getStatus());
+ assertIntervalBetweenDatesIsLessThan(requestHangingTime, serverAskedToStopInstant, Instant.now());
+ assertEquals("hello, world", testClient.responseText);
+ }
+
+ private static void assertIntervalBetweenDatesEquals(final Duration expectedDuration, final Instant firstInstant, final Instant secondInstant) {
+ final Duration dateDifference = Duration.between(firstInstant, secondInstant)
+ .abs();
+
+ final Duration tolerance = Duration.ofMillis(200); // let's consider that +/- 200ms is fine
+ final boolean isDateDifferenceNearlyEqualToExpectedDuration = dateDifference.minus(expectedDuration)
+ .abs()
+ .minus(tolerance)
+ .isNegative();
+ assertTrue(isDateDifferenceNearlyEqualToExpectedDuration, String.format("Expected delay: %d second(s) versus %d second(s)\n", expectedDuration.toMillis(), dateDifference.toMillis()));
+ }
+
+ private static void assertIntervalBetweenDatesIsLessThan(final Duration expectedDuration, final Instant firstInstant, final Instant secondInstant) {
+ final Duration dateDifference = Duration.between(firstInstant, secondInstant).abs();
+
+ final Duration tolerance = Duration.ofMillis(200); // let's consider that +/- 200ms is fine
+ final boolean dateDifferenceIsLessThanExpectedDuration = dateDifference.minus(expectedDuration)
+ .abs()
+ .minus(tolerance)
+ .isNegative();
+ assertTrue(dateDifferenceIsLessThanExpectedDuration, String.format("difference between dates [%d second(s)] is higher than expected: %d second(s)\n", dateDifference.getSeconds(), expectedDuration.getSeconds()));
+ }
+
+ private Server startServerWithoutGracefulShutdown(final Restlet restlet) throws Exception {
+ return startServer(false, Duration.ZERO, restlet);
+ }
+
+ private Server startServerWithGracefulShutdown(final Duration timeout, final Restlet restlet) throws Exception {
+ return startServer(true, timeout, restlet);
+ }
+
+ private Server startServer(final boolean graceful, final Duration timeout, final Restlet restlet) throws Exception {
+
+ Engine.getInstance().getRegisteredServers().add(0, new HttpServerHelper(null)); // Creates a Jetty server helper manually
+ // 0 port means it will be computed when the server starts
+ Server server = new Server(new Context(), singletonList(Protocol.HTTP), null, 0, restlet, HttpServerHelper.class.getCanonicalName());
+
+ if (shouldDebug) {
+ server.getContext().getParameters().add("tracing", "true");
+ System.setProperty("org.eclipse.jetty.LEVEL", "TRACE");
+ System.setProperty("sun.net.www.protocol.http.HttpURLConnection.LEVEL", "ALL");
+ Engine.setLogLevel(Level.FINE);
+ }
+
+ if (graceful) {
+ // Don't let the lowResource monitor mess with the current test
+ server.getContext().getParameters().add("lowResource.idleTimeout", Long.toString(timeout.toMillis() * 10));
+ }
+ server.getContext().getParameters().add("shutdown.gracefully", Boolean.toString(graceful));
+ server.getContext().getParameters().add("shutdown.timeout", Long.toString(timeout.toMillis()));
+
+ server.start();
+ log( "Server started on port " + server.getEphemeralPort());
+ return server;
+ }
+
+ /**
+ * Creates a resource that hangs for a specific amount of time before answering and acknowledges incoming requests
+ * by unlocking the given lock.
+ */
+ private static Restlet newHangingAndLockedRestlet(final Duration requestHangingTime, final Lock lock) {
+ return new Restlet() {
+ @Override
+ public void handle(final Request request, final Response response) {
+ log("Restlet opens lock");
+ lock.unlock();
+ log("Restlet starts sleeping");
+ try {
+ Thread.sleep(requestHangingTime.toMillis());
+ } catch (Exception e) {
+ // silently stops, especially when Jetty server will abruptly quit after time out
+ LOGGER.log(Level.FINE, "Restlet error", e);
+ }
+ log("Restlet woke up, answering");
+ response.setEntity("hello, world", MediaType.TEXT_ALL);
+ }
+ };
+ }
+
+ /**
+ * Creates a resource that hangs for a specific amount of time before answering.
+ */
+ private static Restlet newHangingRestlet(final Duration requestHangingTime) {
+ return new Restlet() {
+ @Override
+ public void handle(final Request request, final Response response) {
+ try {
+ Thread.sleep(requestHangingTime.toMillis());
+ } catch (Exception e) {
+ // silently stops, especially when Jetty server will abruptly quit after time out
+ LOGGER.log(Level.FINE, "Restlet error", e);
+ }
+ LOGGER.log(Level.FINE, "Restlet woke up, answering");
+ response.setEntity("hello, world", MediaType.TEXT_ALL);
+ }
+ };
+ }
+
+ private static class TestClient implements Runnable {
+ private final ClientResource cr;
+ private Instant stoppedAt = null;
+ private final Lock lock; // ensures that the client has totally run
+ private String responseText;
+
+ public TestClient(final Server server) {
+ cr = new ClientResource("http://localhost:" + server.getEphemeralPort());
+ cr.setRetryOnError(false);
+ this.lock = new Lock("TestClient");
+ }
+
+ @Override
+ public void run() {
+ try {
+ LOGGER.log(Level.FINE, "Client running");
+ responseText = cr.get().getText();
+ } catch (Exception e) { // silently ignore errors
+ LOGGER.log(Level.FINE, "Client error", e);
+ } finally {
+ stoppedAt = Instant.now();
+ lock.unlock();
+ LOGGER.log(Level.FINE, "Client state: " + cr.getStatus());
+ }
+ }
+ }
+
+ private synchronized Instant stopServer(final Server server) {
+ log("Server stopping");
+ Instant serverAskedToStopInstant = Instant.now();
+ try {
+ final HttpServerHelper serverHelper = (HttpServerHelper) server.getContext().getAttributes().get("org.restlet.engine.helper");
+ serverHelper.getWrappedServer().stop();
+ log("Server stopped");
+ } catch (Exception e) {
+ // silently ignore errors
+ log("Server stopped", e);
+ }
+ return serverAskedToStopInstant;
+ }
+
+ /**
+ * Returns the effective Jetty timeout since there is an extra half-timeout in the {@link org.eclipse.jetty.util.thread.QueuedThreadPool}.
+ */
+ private Duration toJettyEffectiveTimeout(final Duration timeout) {
+ return timeout.plusMillis(500); // FIXME: needs improvements
+ }
+
+ private static void log(final String message) {
+ LOGGER.info(Instant.now().toString() + " " + message);
+ }
+
+ private static void log(final String message, final Exception exception) {
+ LOGGER.log(Level.INFO, Instant.now().toString() + " " + message, exception);
+ }
+
+}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/BaseConnectorsTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/BaseConnectorsTestCase.java
index c263d83328..0eed13fe13 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/BaseConnectorsTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/BaseConnectorsTestCase.java
@@ -11,15 +11,16 @@
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
-import org.restlet.Application;
-import org.restlet.Component;
-import org.restlet.Server;
+import org.restlet.*;
import org.restlet.data.Protocol;
import org.restlet.engine.Engine;
import org.restlet.engine.adapter.HttpServerHelper;
import org.restlet.engine.connector.ClientHelper;
+import org.restlet.engine.connector.HttpClientHelper;
import org.restlet.engine.connector.ServerHelper;
+import java.util.List;
+import java.util.logging.Level;
import java.util.stream.Stream;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
@@ -32,57 +33,103 @@
* @author Jerome Louvel
*/
public abstract class BaseConnectorsTestCase {
-
private Component component;
private int port;
- protected abstract void doTestUri(String uri) throws Exception;
+ /**
+ * The method to implement in the tests
+ *
+ * @param serverPort The port that the server is listening to.
+ * @throws Exception
+ */
+ protected abstract void doTest(final int serverPort) throws Exception;
+
+ protected boolean shouldDebug() {
+ return false;
+ }
- protected Server configureServer(final Component component) {
- // server.getContext().getParameters().add("tracing", "true");
+ protected Server createServer(final Component component) {
return component.getServers().add(Protocol.HTTP, 0);
}
- protected abstract Application createApplication();
+ protected void configureServer(final Server server) {
+ server.getContext().getParameters().add("threadPool.minThreads", "1");
+ server.getContext().getParameters().add("threadPool.maxThreads", "10");
+ server.getContext().getParameters().add("shutdown.gracefully", "false");
- protected String getCallUri(final int port) {
- return "http://localhost:" + port + "/test";
+ if (shouldDebug()) {
+ server.getContext().getParameters().add("tracing", "true");
+ }
}
- protected Stream listTestCases() {
- return Stream.of(
- // new ConnectorTestCase(HttpServer.INTERNAL_HTTP, HttpClient.JETTY), // restore while taking care of #1444
- // new ConnectorTestCase(HttpServer.JETTY_HTTP, HttpClient.INTERNAL), // restore while taking care of #1444
- // new ConnectorTestCase(HttpServer.JETTY_HTTP, HttpClient.JETTY), // restore while taking care of #1444
- new ConnectorTestCase(HttpServer.INTERNAL_HTTP, HttpClient.INTERNAL)
+ protected abstract Application createApplication();
+
+ protected List listTestCases() {
+ return List.of(
+ new ConnectorTestCase(HttpServer.JETTY_HTTP, HttpClient.INTERNAL),
+ new ConnectorTestCase(HttpServer.INTERNAL_HTTP, HttpClient.INTERNAL),
+ new ConnectorTestCase(HttpServer.INTERNAL_HTTP, HttpClient.JETTY),
+ new ConnectorTestCase(HttpServer.JETTY_HTTP, HttpClient.JETTY)
);
}
@TestFactory
- Stream dynamicTestsFromStream() {
- return listTestCases()
+ Stream testsFactory() {
+ return listTestCases().stream()
.map(testCase -> dynamicTest(
testCase.getTestLabel(),
- () -> {
- runTest(testCase.httpServer, testCase.httpClient);
- resetEngine();
- }));
+ () -> runTest(testCase.httpServer, testCase.httpClient)));
}
private void runTest(final HttpServer server, final HttpClient client) throws Exception {
- // Engine.setLogLevel(Level.FINE);
- Engine nre = Engine.register(false);
- nre.getRegisteredServers().add(server.serverHelper);
- nre.getRegisteredClients().add(client.clientHelper);
- nre.registerDefaultAuthentications();
- nre.registerDefaultConverters();
+ if (shouldDebug()) {
+ System.setProperty("org.eclipse.jetty.LEVEL", "TRACE");
+ System.setProperty("sun.net.www.protocol.http.HttpURLConnection.LEVEL", "ALL");
+ }
+ initEngine(server, client);
start();
try {
- doTestUri(getCallUri(port));
+ doTest(port);
} finally {
stop();
+ resetEngine();
+ }
+ }
+
+ private void start() throws Exception {
+ this.component = new Component();
+ final Server server = createServer(this.component);
+ configureServer(server);
+ Application application = createApplication();
+
+ this.component.getDefaultHost().attach(application);
+ this.component.start();
+ this.port = server.getEphemeralPort();
+ }
+
+ private void stop() throws Exception {
+ if ((this.component != null) && this.component.isStarted()) {
+ this.component.stop();
+ }
+ this.component = null;
+ }
+
+ private void initEngine(HttpServer server, HttpClient client) {
+ if (shouldDebug()) {
+ Engine.setLogLevel(Level.FINE);
}
+
+ Engine nre = Engine.register(false);
+ nre.getRegisteredServers().add(server.serverHelper);
+ nre.getRegisteredClients().add(client.clientHelper);
+ nre.registerDefaultAuthentications();
+ nre.registerDefaultConverters();
+ }
+
+ private void resetEngine() {
+ // Restore a clean engine
+ org.restlet.engine.Engine.register();
}
public enum HttpServer {
@@ -99,7 +146,7 @@ public enum HttpServer {
}
public enum HttpClient {
- INTERNAL(new org.restlet.engine.connector.HttpClientHelper(null)), JETTY(new org.restlet.ext.jetty.HttpClientHelper(null));
+ INTERNAL(new HttpClientHelper(null)), JETTY(new org.restlet.ext.jetty.HttpClientHelper(null));
final ClientHelper clientHelper;
@@ -108,26 +155,4 @@ public enum HttpClient {
}
}
- private void start() throws Exception {
- this.component = new Component();
- Server server = configureServer(this.component);
- Application application = createApplication();
-
- this.component.getDefaultHost().attach(application);
- this.component.start();
- this.port = server.getEphemeralPort();
- }
-
- private void stop() throws Exception {
- if ((this.component != null) && this.component.isStarted()) {
- this.component.stop();
- }
- this.component = null;
- }
-
- private void resetEngine() {
- // Restore a clean engine
- org.restlet.engine.Engine.register();
- }
-
}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingPutTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingPutTestCase.java
index 618d868d49..5d37c49c73 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingPutTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingPutTestCase.java
@@ -19,6 +19,7 @@
import org.restlet.resource.ServerResource;
import org.restlet.routing.Router;
+import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -32,7 +33,9 @@ public class ChunkedEncodingPutTestCase extends BaseConnectorsTestCase {
private static final int LOOP_NUMBER = 200;
@Override
- protected void doTestUri(String uri) throws Exception {
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d", serverPort);
+
for (int testIndex = 0; testIndex < LOOP_NUMBER; testIndex++) {
sendPut(testIndex, uri, 10);
}
@@ -50,7 +53,7 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", PutTestResource.class);
+ router.attachDefault(PutTestResource.class);
return router;
}
};
@@ -95,12 +98,12 @@ private void sendPut(int testIndex, final String uri, final int size) throws Exc
System.out.println(response.getStatus());
}
- assertNotNull(response.getEntity(), String.format("test #%d - size %d: response's entity is null", testIndex, size));
+ assertNotNull(response.getEntity(), format("test #%d - size %d: response's entity is null", testIndex, size));
final String responseEntity = response.getEntity().getText();
- assertNotNull(responseEntity, String.format("test #%d - size %d: response's entity content is null", testIndex, size));
- assertEquals(size, responseEntity.length(), String.format("test #%d - size %d: length of response's entity is wrong", testIndex, size));
+ assertNotNull(responseEntity, format("test #%d - size %d: response's entity content is null", testIndex, size));
+ assertEquals(size, responseEntity.length(), format("test #%d - size %d: length of response's entity is wrong", testIndex, size));
final String expectedResponseEntity = createChunkedRepresentation(size).getText();
- assertEquals(expectedResponseEntity, responseEntity, String.format("test #%d - size %d: response's entity is wrong", testIndex, size));
+ assertEquals(expectedResponseEntity, responseEntity, format("test #%d - size %d: response's entity is wrong", testIndex, size));
} finally {
response.release();
client.stop();
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingTestCase.java
index 28124fe493..78667d6005 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ChunkedEncodingTestCase.java
@@ -21,6 +21,7 @@
import java.io.IOException;
+import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.*;
/**
@@ -37,7 +38,7 @@ private static void assertXML(int testIndex, Representation entity) {
try {
String expected = "";
String text = entity.getText();
- assertEquals(expected, text, String.format("test #%d: xml representation is wrong", testIndex));
+ assertEquals(expected, text, format("test #%d: xml representation is wrong", testIndex));
} catch (IOException ex) {
fail(ex.getMessage());
}
@@ -59,7 +60,9 @@ private static Representation createTestXml() {
}
@Override
- protected void doTestUri(String uri) throws Exception {
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d", serverPort);
+
for (int testIndex = 0; testIndex < LOOP_NUMBER; testIndex++) {
sendGet(testIndex, uri);
sendPut(testIndex, uri);
@@ -72,7 +75,7 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", PutTestResource.class);
+ router.attachDefault(PutTestResource.class);
return router;
}
};
@@ -84,7 +87,7 @@ private void sendGet(int testIndex, String uri) throws Exception {
final Response response = client.handle(request);
try {
- assertEquals(Status.SUCCESS_OK, response.getStatus(), String.format("test #%d: response's status is wrong", testIndex));
+ assertEquals(Status.SUCCESS_OK, response.getStatus(), format("test #%d: response's status is wrong", testIndex));
assertXML(testIndex, response.getEntity());
} finally {
response.release();
@@ -99,7 +102,7 @@ private void sendPut(int testIndex, String uri) throws Exception {
try {
assertChunkedHeader(response);
- assertEquals(Status.SUCCESS_OK, response.getStatus(), String.format("test #%d: response's status is wrong", testIndex));
+ assertEquals(Status.SUCCESS_OK, response.getStatus(), format("test #%d: response's status is wrong", testIndex));
assertXML(testIndex, response.getEntity());
} finally {
response.release();
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetChunkedTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetChunkedTestCase.java
index 2a98395d6b..ec85ec8285 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetChunkedTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetChunkedTestCase.java
@@ -18,6 +18,7 @@
import org.restlet.resource.ServerResource;
import org.restlet.routing.Router;
+import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -29,8 +30,11 @@
public class GetChunkedTestCase extends BaseConnectorsTestCase {
private static final String text = "" + "a".repeat(1000) + "";
+
@Override
- protected void doTestUri(String uri) throws Exception {
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d", serverPort);
+
final Client client = new Client(Protocol.HTTP);
final Request request = new Request(Method.GET, uri);
final Response response = client.handle(request);
@@ -51,7 +55,7 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", GetChunkedTestResource.class);
+ router.attachDefault(GetChunkedTestResource.class);
return router;
}
};
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetQueryParamTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetQueryParamTestCase.java
index 0f4a1b1be5..113fa028c1 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetQueryParamTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetQueryParamTestCase.java
@@ -22,6 +22,7 @@
import java.util.SortedMap;
import java.util.TreeMap;
+import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
@@ -32,12 +33,9 @@
public class GetQueryParamTestCase extends BaseConnectorsTestCase {
@Override
- protected String getCallUri(int port) {
- return super.getCallUri(port) + "?q1=a&q2=b";
- }
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d?q1=a&q2=b", serverPort);
- @Override
- protected void doTestUri(String uri) throws Exception {
final Client client = new Client(Protocol.HTTP);
final Request request = new Request(Method.GET, uri);
final Response response = client.handle(request);
@@ -56,7 +54,7 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", GetTestResource.class);
+ router.attachDefault(GetTestResource.class);
return router;
}
};
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetTestCase.java
index 6538e659f6..b4f6366aaf 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/GetTestCase.java
@@ -9,13 +9,7 @@
package org.restlet.ext.jetty.connectors;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.restlet.Application;
-import org.restlet.Client;
-import org.restlet.Request;
-import org.restlet.Response;
-import org.restlet.Restlet;
+import org.restlet.*;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Status;
@@ -23,6 +17,9 @@
import org.restlet.resource.ServerResource;
import org.restlet.routing.Router;
+import static java.lang.String.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
/**
* Test that a simple get works for all the connectors.
*
@@ -31,7 +28,9 @@
public class GetTestCase extends BaseConnectorsTestCase {
@Override
- protected void doTestUri(String uri) throws Exception {
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d", serverPort);
+
final Request request = new Request(Method.GET, uri);
final Client client = new Client(Protocol.HTTP);
final Response response = client.handle(request);
@@ -54,7 +53,7 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", GetTestResource.class);
+ router.attachDefault(GetTestResource.class);
return router;
}
};
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/PostPutTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/PostPutTestCase.java
index b7b8d5544c..fbc273b266 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/PostPutTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/PostPutTestCase.java
@@ -9,6 +9,7 @@
package org.restlet.ext.jetty.connectors;
+import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -30,7 +31,9 @@
public class PostPutTestCase extends BaseConnectorsTestCase {
@Override
- protected void doTestUri(String uri) throws Exception {
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d", serverPort);
+
final Client client = new Client(Protocol.HTTP);
try {
testCall(client, Method.POST, uri);
@@ -50,7 +53,7 @@ public Restlet createInboundRoot() {
public void handle(Request request, Response response) {
Representation entity = request.getEntity();
if (entity != null) {
- Form form = new Form(entity);
+ final Form form = new Form(entity);
response.setEntity(form.getWebRepresentation());
}
}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/RemoteClientAddressTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/RemoteClientAddressTestCase.java
index b7bc4fb8e6..dbb72066ed 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/RemoteClientAddressTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/RemoteClientAddressTestCase.java
@@ -9,19 +9,7 @@
package org.restlet.ext.jetty.connectors;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.Enumeration;
-
-import org.restlet.Application;
-import org.restlet.Client;
-import org.restlet.Request;
-import org.restlet.Response;
-import org.restlet.Restlet;
+import org.restlet.*;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
@@ -32,6 +20,15 @@
import org.restlet.resource.ServerResource;
import org.restlet.routing.Router;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+import static java.lang.String.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
/**
* Test that the client address is available for all the connectors
*
@@ -40,13 +37,16 @@
public class RemoteClientAddressTestCase extends BaseConnectorsTestCase {
@Override
- protected void doTestUri(String uri) throws Exception {
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d", serverPort);
+
final Client client = new Client(Protocol.HTTP);
final Request request = new Request(Method.GET, uri);
final Response response = client.handle(request);
try {
assertEquals(Status.SUCCESS_OK, response.getStatus());
+ assertEquals("OK", response.getEntityAsText());
} finally {
response.release();
client.stop();
@@ -59,7 +59,7 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", RemoteClientAddressResource.class);
+ router.attachDefault(RemoteClientAddressResource.class);
return router;
}
};
@@ -74,15 +74,15 @@ public RemoteClientAddressResource() {
@Override
public Representation get(Variant variant) {
boolean localAddress = false;
+
try {
- Enumeration n = NetworkInterface
- .getNetworkInterfaces();
- for (; n.hasMoreElements();) {
- NetworkInterface e = n.nextElement();
- Enumeration a = e.getInetAddresses();
- for (; a.hasMoreElements();) {
- InetAddress addr = a.nextElement();
- if (addr.getHostAddress().equals(
+ Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ while (networkInterfaces.hasMoreElements()) {
+ NetworkInterface networkInterface = networkInterfaces.nextElement();
+ Enumeration inetAddresses = networkInterface.getInetAddresses();
+ while (inetAddresses.hasMoreElements()) {
+ final InetAddress inetAddress = inetAddresses.nextElement();
+ if (inetAddress.getHostAddress().equals(
getRequest().getClientInfo().getAddress())) {
localAddress = true;
}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ServerMaxConnectionsTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ServerMaxConnectionsTestCase.java
new file mode 100644
index 0000000000..5319fb13ab
--- /dev/null
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/ServerMaxConnectionsTestCase.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright 2005-2024 Qlik
+ *
+ * The contents of this file is subject to the terms of the Apache 2.0 open
+ * source license available at http://www.opensource.org/licenses/apache-2.0
+ *
+ * Restlet is a registered trademark of QlikTech International AB.
+ */
+
+package org.restlet.ext.jetty.connectors;
+
+import org.restlet.*;
+import org.restlet.data.Method;
+import org.restlet.data.Parameter;
+import org.restlet.data.Protocol;
+import org.restlet.data.Status;
+import org.restlet.engine.connector.HttpClientHelper;
+import org.restlet.util.Series;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+import static java.lang.String.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * This tests the ability of the server to accept a fixed number of incoming connections.
+ */
+public class ServerMaxConnectionsTestCase extends BaseConnectorsTestCase {
+ private static final Logger LOGGER = Logger.getLogger(ServerMaxConnectionsTestCase.class.getCanonicalName());
+ private final static int CONNECTIONS_NUMBER = 1;
+ private final static int CONCURRENT_REQUESTS = 2;
+ private final static Duration SERVER_RESOURCE_FREEZE_DURATION = Duration.ofSeconds(1);
+
+ @Override
+ protected void configureServer(final Server server) {
+ super.configureServer(server);
+
+ final Series parameters = server.getContext().getParameters();
+ parameters.add("server.maxConnections", Integer.toString(CONNECTIONS_NUMBER));
+ parameters.add("connector.acceptors", Integer.toString(CONCURRENT_REQUESTS)); // server can accept all requests
+ }
+
+ @Override
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("http://localhost:%d", serverPort);
+
+ final CountDownLatch countDownLatch = new CountDownLatch(CONCURRENT_REQUESTS);
+
+ final List testResults = new ArrayList<>();
+
+ final Executor executor = Executors.newFixedThreadPool(CONCURRENT_REQUESTS);
+ final Runnable runnable = () -> {
+ testResults.add(sendGet(uri).isSuccess());
+ countDownLatch.countDown();
+ log("client countDownLatch.countDown done " + Thread.currentThread().getName());
+ };
+
+ for (int i = 0; i < CONCURRENT_REQUESTS; i++) {
+ executor.execute(runnable);
+ }
+
+ log("test before countDownLatch.await()");
+ countDownLatch.await();
+ log("test after countDownLatch.await()");
+
+ assertEquals(CONCURRENT_REQUESTS, testResults.size());
+ assertTrue(testResults.contains(true)); // One has succeeded
+ assertTrue(testResults.contains(false)); // The other has failed
+ }
+
+ private static void log(final String message) {
+ LOGGER.fine(message + " " + Thread.currentThread());
+ }
+
+ @Override
+ protected List listTestCases() {
+ return List.of(
+ new ConnectorTestCase(HttpServer.JETTY_HTTP, HttpClient.JETTY),
+ new ConnectorTestCase(HttpServer.JETTY_HTTP, HttpClient.INTERNAL)
+ );
+ }
+
+ private Status sendGet(final String uri) {
+ final Status result;
+
+ log("client send get " + Thread.currentThread().getName());
+ try {
+ final Request request = new Request(Method.GET, uri + "/" + Thread.currentThread().getName());
+ final Client client = new Client(new Context(), Protocol.HTTP);
+
+ if (client.getContext().getAttributes().get("org.restlet.engine.helper") instanceof HttpClientHelper) {
+ // Specific to the internal client
+ // When Jetty refuses the extra connection, this does not block the underlying native HttpClient...
+ // Let's set a timeout higher than the wait time imposed by the server (otherwise all requests will fail)
+ String readTimeoutInMs = Long.toString(SERVER_RESOURCE_FREEZE_DURATION.plus(Duration.ofSeconds(1)).toMillis());
+ client.getContext().getParameters().add("readTimeout", readTimeoutInMs);
+ }
+
+ final Response response = client.handle(request);
+ log("client get sent " + Thread.currentThread().getName());
+ result = response.getStatus();
+ log("client status " + result + " " + Thread.currentThread().getName());
+ response.release();
+ client.stop();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ log("client done " + Thread.currentThread().getName());
+ return result;
+ }
+
+ @Override
+ protected Application createApplication() {
+ return new Application() {
+ @Override
+ public Restlet createInboundRoot() {
+ return new Restlet() {
+ @Override
+ public void handle(Request request, Response response) {
+ try {
+ log("server resource wait");
+ Thread.sleep(SERVER_RESOURCE_FREEZE_DURATION.toMillis());
+ log("server resource wait ended");
+ response.setStatus(Status.SUCCESS_NO_CONTENT);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+ };
+ }
+
+}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslBaseConnectorsTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslBaseConnectorsTestCase.java
index bc23de2422..d06037f6f3 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslBaseConnectorsTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslBaseConnectorsTestCase.java
@@ -10,11 +10,8 @@
package org.restlet.ext.jetty.connectors;
import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
import org.restlet.*;
-import org.restlet.data.Method;
import org.restlet.data.Parameter;
import org.restlet.data.Protocol;
import org.restlet.engine.io.IoUtils;
@@ -22,7 +19,7 @@
import java.io.*;
import java.nio.file.Files;
-import java.util.stream.Stream;
+import java.util.List;
/**
* Base test case that will call an abstract method for several client/server
@@ -35,35 +32,17 @@
@SuppressWarnings("unused")
public abstract class SslBaseConnectorsTestCase extends BaseConnectorsTestCase {
+ protected static final String KEYSTORE_FILE_NAME = "dummy.p12";
+ protected static final String KEYSTORE_PASSWORD = "testtest";
+ protected static final String KEYSTORE_TYPE = "PKCS12";
protected static File testKeystoreFile;
- protected void configureSslClientParameters(Context context) {
- Series parameters = context.getParameters();
- parameters.add("truststorePath", testKeystoreFile.getPath());
- parameters.add("truststorePassword", "testtest");
- }
-
- protected void configureSslServerParameters(Context context) {
- Series parameters = context.getParameters();
- parameters.add("keystorePath", testKeystoreFile.getPath());
- parameters.add("keystorePassword", "testtest");
- parameters.add("keyPassword", "testtest");
- parameters.add("truststorePath", testKeystoreFile.getPath());
- parameters.add("truststorePassword", "testtest");
- // parameters.add("tracing", "true");
- }
-
- @Override
- protected String getCallUri(final int port) {
- return "https://localhost:" + port + "/test";
- }
-
@BeforeAll
public static void globalSetUp() throws IOException {
- testKeystoreFile = Files.createTempFile("sslBaseConnectorsTest", "dummy.jks").toFile();
+ testKeystoreFile = Files.createTempFile("sslBaseConnectorsTest", KEYSTORE_FILE_NAME).toFile();
testKeystoreFile.delete();
- InputStream resourceAsStream = SslBaseConnectorsTestCase.class.getResourceAsStream("dummy.jks");
+ InputStream resourceAsStream = SslBaseConnectorsTestCase.class.getResourceAsStream(KEYSTORE_FILE_NAME);
if (resourceAsStream != null) {
OutputStream outputStream = new FileOutputStream(testKeystoreFile);
IoUtils.copy(resourceAsStream, outputStream);
@@ -76,21 +55,53 @@ public static void globalSetUp() throws IOException {
}
@Override
- protected Stream listTestCases() {
- return Stream.of(
+ protected List listTestCases() {
+ return List.of(
new ConnectorTestCase(HttpServer.INTERNAL_HTTPS, HttpClient.JETTY),
- // new ConnectorTestCase(HttpServer.JETTY_HTTPS, HttpClient.INTERNAL), // restore while taking care of #1444
- // new ConnectorTestCase(HttpServer.JETTY_HTTPS, HttpClient.JETTY), // restore while taking care of #1444
+ new ConnectorTestCase(HttpServer.JETTY_HTTPS, HttpClient.INTERNAL),
+ new ConnectorTestCase(HttpServer.JETTY_HTTPS, HttpClient.JETTY),
new ConnectorTestCase(HttpServer.INTERNAL_HTTPS, HttpClient.INTERNAL)
);
}
@Override
- protected Server configureServer(final Component component) {
- final Server server = component.getServers().add(Protocol.HTTPS, 0);
- configureSslServerParameters(server.getContext());
- // server.getContext().getParameters().add("tracing", "true");
- return server;
+ protected Server createServer(Component component) {
+ return component.getServers().add(Protocol.HTTPS, 0);
+ }
+
+ @Override
+ protected void configureServer(final Server server) {
+ super.configureServer(server);
+ configureSslServerParameters(server);
+ }
+
+ protected void configureSslClientParameters(final Client client) {
+ Series parameters = client.getContext().getParameters();
+
+ parameters.add("truststorePath", testKeystoreFile.getPath());
+ parameters.add("truststorePassword", KEYSTORE_PASSWORD);
+ parameters.add("trustStoreType", KEYSTORE_TYPE);
+ if (shouldDebug()) {
+ parameters.add("tracing", "true");
+ }
+ }
+
+ protected void configureSslServerParameters(final Server server) {
+ Series parameters = server.getContext().getParameters();
+
+ parameters.add("keystorePath", testKeystoreFile.getPath());
+ parameters.add("keystorePassword", KEYSTORE_PASSWORD);
+ parameters.add("keyStoreType", KEYSTORE_TYPE);
+ parameters.add("keyPassword", KEYSTORE_PASSWORD);
+
+ parameters.add("truststorePath", testKeystoreFile.getPath());
+ parameters.add("truststorePassword", KEYSTORE_PASSWORD);
+ parameters.add("trustStoreType", KEYSTORE_TYPE);
+
+ if (shouldDebug()) {
+ System.setProperty("javax.net.debug", "ssl:handshake");
+ parameters.add("tracing", "true");
+ }
}
@AfterAll
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslClientContextGetTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslClientContextGetTestCase.java
index 244f5db8ff..1b879632bf 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslClientContextGetTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslClientContextGetTestCase.java
@@ -9,23 +9,17 @@
package org.restlet.ext.jetty.connectors;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.restlet.Application;
-import org.restlet.Client;
-import org.restlet.Context;
-import org.restlet.Request;
-import org.restlet.Response;
-import org.restlet.Restlet;
-import org.restlet.data.MediaType;
-import org.restlet.data.Method;
-import org.restlet.data.Protocol;
-import org.restlet.data.Status;
+import org.restlet.*;
+import org.restlet.data.*;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.Variant;
import org.restlet.resource.ServerResource;
import org.restlet.routing.Router;
+import org.restlet.util.Series;
+
+import static java.lang.String.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Test that a simple get using SSL works for all the connectors.
@@ -36,20 +30,33 @@
public class SslClientContextGetTestCase extends SslBaseConnectorsTestCase {
@Override
- protected void doTestUri(String uri) throws Exception {
- final Request request = new Request(Method.GET, uri);
- final Client client = new Client(Protocol.HTTPS);
- if (client.getContext() == null) {
- client.setContext(new Context());
- }
- configureSslServerParameters(client.getContext());
- final Response response = client.handle(request);
+ protected void doTest(final int serverPort) throws Exception {
+ final String uri = format("https://localhost:%d", serverPort);
+
+ final Response response = sendGet(uri);
assertEquals(Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription());
assertEquals("Hello world", response.getEntity().getText());
+ }
+
+ private Response sendGet(final String uri) {
+ final Client client = new Client(Protocol.HTTPS);
+ client.setContext(new Context());
+ configureSslClientParameters(client);
+
+ final Request request = new Request(Method.GET, uri);
+ return client.handle(request);
+ }
+
+ @Override
+ protected void configureSslClientParameters(final Client client) {
+ super.configureSslClientParameters(client);
- Thread.sleep(200);
- client.stop();
+ Series parameters = client.getContext().getParameters();
+ parameters.add("keystorePath", testKeystoreFile.getPath());
+ parameters.add("keystorePassword", KEYSTORE_PASSWORD);
+ parameters.add("keyPassword", KEYSTORE_PASSWORD);
+ parameters.add("keyStoreType", KEYSTORE_TYPE);
}
@Override
@@ -58,7 +65,7 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", GetTestResource.class);
+ router.attachDefault(GetTestResource.class);
return router;
}
};
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java
index e9140a9037..65935aef4a 100644
--- a/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java
+++ b/org.restlet.java/org.restlet.ext.jetty/src/test/java/org/restlet/ext/jetty/connectors/SslGetTestCase.java
@@ -9,22 +9,16 @@
package org.restlet.ext.jetty.connectors;
-import org.restlet.Application;
-import org.restlet.Client;
-import org.restlet.Context;
-import org.restlet.Request;
-import org.restlet.Response;
-import org.restlet.Restlet;
-import org.restlet.data.MediaType;
-import org.restlet.data.Method;
-import org.restlet.data.Protocol;
-import org.restlet.data.Status;
+import org.restlet.*;
+import org.restlet.data.*;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.Variant;
import org.restlet.resource.ServerResource;
import org.restlet.routing.Router;
+import org.restlet.util.Series;
+import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
@@ -35,32 +29,21 @@
*/
public class SslGetTestCase extends SslBaseConnectorsTestCase {
- public static class GetTestResource extends ServerResource {
-
- public GetTestResource() {
-
- getVariants().add(new Variant(MediaType.TEXT_PLAIN));
- }
+ @Override
+ protected void doTest(final int serverPort) throws Exception {
+ final Response response = sendGet(format("https://localhost:%d", serverPort));
- @Override
- public Representation get(Variant variant) {
- return new StringRepresentation("Hello world", MediaType.TEXT_PLAIN);
- }
+ assertEquals(Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription());
+ assertEquals("Hello world", response.getEntity().getText());
}
- @Override
- protected void doTestUri(String uri) throws Exception {
- final Request request = new Request(Method.GET, uri);
+ private Response sendGet(final String uri) {
final Client client = new Client(Protocol.HTTPS);
client.setContext(new Context());
- configureSslClientParameters(client.getContext());
- final Response r = client.handle(request);
-
- assertEquals(Status.SUCCESS_OK, r.getStatus(), r.getStatus().getDescription());
- assertEquals("Hello world", r.getEntity().getText());
+ configureSslClientParameters(client);
- Thread.sleep(200);
- client.stop();
+ final Request request = new Request(Method.GET, uri);
+ return client.handle(request);
}
@Override
@@ -69,9 +52,22 @@ protected Application createApplication() {
@Override
public Restlet createInboundRoot() {
final Router router = new Router(getContext());
- router.attach("/test", GetTestResource.class);
+ router.attachDefault(GetTestResource.class);
return router;
}
};
}
+
+ public static class GetTestResource extends ServerResource {
+
+ public GetTestResource() {
+ getVariants().add(new Variant(MediaType.TEXT_PLAIN));
+ }
+
+ @Override
+ public Representation get(Variant variant) {
+ return new StringRepresentation("Hello world", MediaType.TEXT_PLAIN);
+ }
+ }
+
}
diff --git a/org.restlet.java/org.restlet.ext.jetty/src/test/resources/org/restlet/ext/jetty/connectors/dummy.p12 b/org.restlet.java/org.restlet.ext.jetty/src/test/resources/org/restlet/ext/jetty/connectors/dummy.p12
new file mode 100644
index 0000000000..94519e717d
Binary files /dev/null and b/org.restlet.java/org.restlet.ext.jetty/src/test/resources/org/restlet/ext/jetty/connectors/dummy.p12 differ
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java
index 61a4fefd3f..2e0c78e18f 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java
@@ -85,13 +85,7 @@ public ClientAdapter getAdapter() throws Exception {
* @return The connection timeout.
*/
public int getSocketConnectTimeoutMs() {
- int result = 0;
-
- if (getHelpedParameters().getNames().contains("socketConnectTimeoutMs")) {
- result = Integer.parseInt(getHelpedParameters().getFirstValue("socketConnectTimeoutMs", "15000"));
- }
-
- return result;
+ return Integer.parseInt(getHelpedParameters().getFirstValue("socketConnectTimeoutMs", "15000"));
}
@Override
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java
index bae71582a9..2d6174190c 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java
@@ -143,7 +143,7 @@ public void commit(HttpResponse response) {
response.getHttpCall().sendResponse(response);
} catch (Throwable t) {
if (response.getHttpCall().isConnectionBroken(t)) {
- // output a single log line for this common case to avoid filling servers logs
+ // output a single log line for this common case to avoid filling server logs
getLogger().log(Level.INFO,
"The connection was broken. It was probably closed by the client. Reason: " + t.getMessage());
} else {
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java
index 69e9ffa8d9..05562a2374 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java
@@ -12,7 +12,7 @@
import org.restlet.Client;
/**
- * Client connector helper. Base client helper based on NIO non blocking
+ * Client connector helper. Base client helper based on NIO non-blocking
* sockets. Here is the list of parameters that are supported. They should be
* set in the Client's context before it is started:
*
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java
index 9a1e3d8c84..7d450cb4cb 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java
@@ -89,9 +89,9 @@
* the {@link #getHostnameVerifier()} method for details.
*
* Note that by default, the {@link HttpURLConnection} class as implemented by
- * Sun will retry a request if an IO exception is caught, for example due to a
+ * Sun will retry a request if an IO exception is caught, for example, due to a
* connection reset by the server. This can be annoying, especially because the
- * HTTP semantics of non idempotent methods like POST can be broken, but also
+ * HTTP semantics of non-idempotent methods like POST can be broken, but also
* because the new request won't include an entity. There is one way to disable
* this behavior for POST requests only by setting the system property
* "sun.net.http.retryPost" to "false".
@@ -159,7 +159,7 @@ public HostnameVerifier getHostnameVerifier() {
/**
* Returns the read timeout value. A timeout of zero is interpreted as an
- * infinite timeout. Defaults to 60000.
+ * infinite timeout. Default to 60000.
*
* @return The read timeout value.
*/
@@ -178,9 +178,9 @@ public boolean isAllowUserInteraction() {
}
/**
- * Indicates if the protocol will automatically follow redirects.
+ * Indicates if the protocol automatically follows redirects.
*
- * @return True if the protocol will automatically follow redirects.
+ * @return True if the protocol automatically follows redirects.
*/
public boolean isFollowRedirects() {
return Boolean.parseBoolean(getHelpedParameters().getFirstValue("followRedirects", "false"));
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpUrlConnectionCall.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpUrlConnectionCall.java
index 5d35ad8a3b..475414663a 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpUrlConnectionCall.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/connector/HttpUrlConnectionCall.java
@@ -48,7 +48,7 @@ public class HttpUrlConnectionCall extends ClientCall {
* @param helper The parent HTTP client helper.
* @param method The method name.
* @param requestUri The request URI.
- * @param hasEntity Indicates if the call will have an entity to send to the
+ * @param hasEntity Indicates if the call has an entity to send to the
* server.
* @throws IOException
*/
@@ -60,8 +60,7 @@ public HttpUrlConnectionCall(HttpClientHelper helper, String method, String requ
URL url = new URL(requestUri);
this.connection = (HttpURLConnection) url.openConnection();
- // These properties can only be used with Java 1.5 and upper
- // releases
+ // These properties can only be used with Java 1.5 and upper releases
int majorVersionNumber = SystemUtils.getJavaMajorVersion();
int minorVersionNumber = SystemUtils.getJavaMinorVersion();
if ((majorVersionNumber > 1) || ((majorVersionNumber == 1) && (minorVersionNumber >= 5))) {
@@ -201,7 +200,7 @@ public Series getResponseHeaders() {
headerName = getConnection().getHeaderFieldKey(i);
headerValue = getConnection().getHeaderField(i);
} catch (java.util.NoSuchElementException e) {
- // Some implementations especially the one for Google App
+ // Some implementations, especially the one for Google App
// Engine throws a NoSuchElementException though this is not
// stated by the contract of the abstract class
// HttpUrlConnection.
@@ -212,7 +211,7 @@ public Series getResponseHeaders() {
} else {
// As stated by the HttpUrlConnection javadocs, some
// implementations may treat the 0th header field as
- // special, i.e. as the status line returned by the HTTP
+ // special, i.e., as the status line returned by the HTTP
// server.
loop = (i == 0);
}
@@ -249,8 +248,8 @@ public int getStatusCode() throws IOException {
}
/**
- * Sends the request to the client. Commits the request line, headers and
- * optional entity and send them over the network.
+ * Sends the request to the client.
+ * Commits the request line, headers, and optional entity and send them over the network.
*
* @param request The high-level request.
* @return The result status.
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java
index d56b2fa9d4..0b80dce2cd 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java
@@ -268,10 +268,13 @@ public javax.net.ssl.SSLContext createSslContext() throws Exception {
if ((this.keyStorePath != null) || (this.keyStoreProvider != null) || (this.keyStoreType != null)) {
// Loads the key store.
- KeyStore keyStore = (this.keyStoreProvider != null)
- ? KeyStore.getInstance((this.keyStoreType != null) ? this.keyStoreType : KeyStore.getDefaultType(),
- this.keyStoreProvider)
- : KeyStore.getInstance((this.keyStoreType != null) ? this.keyStoreType : KeyStore.getDefaultType());
+ final String nonNullKeyStoreType = (this.keyStoreType != null)
+ ? this.keyStoreType
+ : KeyStore.getDefaultType();
+ final KeyStore keyStore = (this.keyStoreProvider != null)
+ ? KeyStore.getInstance(nonNullKeyStoreType, this.keyStoreProvider)
+ : KeyStore.getInstance(nonNullKeyStoreType);
+
FileInputStream keyStoreInputStream = null;
try {
@@ -294,12 +297,11 @@ public javax.net.ssl.SSLContext createSslContext() throws Exception {
if ((this.trustStorePath != null) || (this.trustStoreProvider != null) || (this.trustStoreType != null)) {
// Loads the trust store.
+ String nonNullTrustStoreType = (this.trustStoreType != null) ? this.trustStoreType : KeyStore.getDefaultType();
KeyStore trustStore = (this.trustStoreProvider != null)
- ? KeyStore.getInstance(
- (this.trustStoreType != null) ? this.trustStoreType : KeyStore.getDefaultType(),
- this.trustStoreProvider)
- : KeyStore.getInstance(
- (this.trustStoreType != null) ? this.trustStoreType : KeyStore.getDefaultType());
+ ? KeyStore.getInstance(nonNullTrustStoreType, this.trustStoreProvider)
+ : KeyStore.getInstance(nonNullTrustStoreType);
+
FileInputStream trustStoreInputStream = null;
try {
@@ -329,7 +331,7 @@ public javax.net.ssl.SSLContext createSslContext() throws Exception {
sslContext.init(kmf != null ? kmf.getKeyManagers() : null, tmf != null ? tmf.getTrustManagers() : null, sr);
// Wraps the SSL context to be able to set cipher suites and other
- // properties after SSL engine creation for example
+ // properties after SSL engine creation, for example
result = createWrapper(sslContext);
return result;
}
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java
index 46233894c8..c799514448 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java
@@ -92,7 +92,7 @@ public static Integer extractKeySize(String sslCipherSuite) {
}
/**
- * Returns the SSL context factory. It first look for a "sslContextFactory"
+ * Returns the SSL context factory. It first looks for a "sslContextFactory"
* attribute (instance), then for a "sslContextFactory" parameter (class name to
* instantiate).
*
@@ -102,7 +102,8 @@ public static Integer extractKeySize(String sslCipherSuite) {
*/
public static SslContextFactory getSslContextFactory(RestletHelper> helper) {
- SslContextFactory result = (SslContextFactory) ((helper.getContext() == null) ? null
+ SslContextFactory result = (SslContextFactory) ((helper.getContext() == null)
+ ? null
: helper.getContext().getAttributes().get("sslContextFactory"));
if (result == null) {
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java
index 1c99c8c0e1..df5339143f 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java
@@ -44,7 +44,7 @@ public CertificateAuthenticator(Context context) {
/**
* Extracts the Principal of the subject to use from a chain of certificate. By
- * default, this is the X500Principal of the subject subject of the first
+ * default, this is the X500Principal of the subject of the first
* certificate in the chain.
*
* @see X509Certificate
@@ -55,7 +55,7 @@ public CertificateAuthenticator(Context context) {
protected List getPrincipals(List certificateChain) {
ArrayList principals = null;
- if ((certificateChain != null) && (certificateChain.size() > 0)) {
+ if ((certificateChain != null) && (!certificateChain.isEmpty())) {
Certificate userCert = certificateChain.get(0);
if (userCert instanceof X509Certificate) {
diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/util/Series.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/util/Series.java
index 3f337826b5..62b7635b59 100644
--- a/org.restlet.java/org.restlet/src/main/java/org/restlet/util/Series.java
+++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/util/Series.java
@@ -205,7 +205,7 @@ public T getFirst(String name, boolean ignoreCase) {
/**
* Returns the value of the first parameter found with the given name.
*
- * @param name The parameter name (case sensitive).
+ * @param name The parameter name (case-sensitive).
* @return The value of the first parameter found with the given name.
*/
public String getFirstValue(String name) {
@@ -216,7 +216,7 @@ public String getFirstValue(String name) {
* Returns the value of the first parameter found with the given name.
*
* @param name The parameter name.
- * @param ignoreCase Indicates if the name comparison is case sensitive.
+ * @param ignoreCase Indicates if the name comparison is case-sensitive.
* @return The value of the first parameter found with the given name.
*/
public String getFirstValue(String name, boolean ignoreCase) {
diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java
index 4222d30e2c..57aa444395 100644
--- a/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java
+++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java
@@ -15,8 +15,8 @@
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.representation.StringRepresentation;
-import org.restlet.routing.Redirector;
+import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
@@ -61,14 +61,11 @@ public void testRedirect() throws Exception {
@Override
public void handle(Request request, Response response) {
// Print the requested URI path
- final String message = "Resource URI: "
- + request.getResourceRef() + '\n' + "Base URI: "
- + request.getResourceRef().getBaseRef() + '\n'
- + "Remaining part: "
- + request.getResourceRef().getRemainingPart() + '\n'
+ final String message = "Resource URI: " + request.getResourceRef() + '\n'
+ + "Base URI: " + request.getResourceRef().getBaseRef() + '\n'
+ + "Remaining part: " + request.getResourceRef().getRemainingPart() + '\n'
+ "Method name: " + request.getMethod() + '\n';
- response.setEntity(new StringRepresentation(message,
- MediaType.TEXT_PLAIN));
+ response.setEntity(new StringRepresentation(message, MediaType.TEXT_PLAIN));
}
};
@@ -87,12 +84,11 @@ public void handle(Request request, Response response) {
// Tests
final Context context = clientComponent.getContext();
- String uri = "http://localhost:" + TEST_PORT + "/?foo=bar";
+ String uri = format("http://localhost:%d/?foo=bar", TEST_PORT);
testCall(context, Method.GET, uri);
testCall(context, Method.DELETE, uri);
- uri = "http://localhost:" + TEST_PORT
- + "/abcd/efgh/ijkl?foo=bar&foo=beer";
+ uri = format("http://localhost:%d/abcd/efgh/ijkl?foo=bar&foo=beer", TEST_PORT);
testCall(context, Method.GET, uri);
testCall(context, Method.DELETE, uri);