From 8ccc23cd635ba39306fab956f2ba599b7922281e Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Mon, 19 Jul 2021 12:00:58 +0200 Subject: [PATCH] Use Java 11 HttpClient Reduce dependencies by using JRE-provided HTTP client. JIRA: NETCONF-751 Change-Id: I49a51306a042ed534bafd7174633e300f00ce305 Signed-off-by: Robert Varga --- netconf/tools/netconf-testtool/pom.xml | 5 - .../netconf/test/tool/Execution.java | 108 ++++++++---------- .../netconf/test/tool/ScaleUtil.java | 37 +++--- .../http/perf/AsyncExecutionStrategy.java | 46 +++----- .../client/http/perf/PerfClientCallable.java | 44 +++---- .../client/http/perf/RequestMessageUtils.java | 35 ++---- .../http/perf/SyncExecutionStrategy.java | 39 +++---- 7 files changed, 129 insertions(+), 185 deletions(-) diff --git a/netconf/tools/netconf-testtool/pom.xml b/netconf/tools/netconf-testtool/pom.xml index 108983bc0d..fa3c8f9c41 100644 --- a/netconf/tools/netconf-testtool/pom.xml +++ b/netconf/tools/netconf-testtool/pom.xml @@ -42,11 +42,6 @@ logback-classic compile - - com.ning - async-http-client - 1.9.40 - org.bouncycastle bcpkix-jdk15on diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/Execution.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/Execution.java index 4b1470a497..208ee76c84 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/Execution.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/Execution.java @@ -7,25 +7,26 @@ */ package org.opendaylight.netconf.test.tool; -import com.ning.http.client.AsyncCompletionHandler; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.HttpResponseStatus; -import com.ning.http.client.Realm; -import com.ning.http.client.Request; -import com.ning.http.client.Response; import java.io.IOException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Semaphore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Execution implements Callable { - private final ArrayList payloads; - private final AsyncHttpClient asyncHttpClient; + private final ArrayList payloads; + private final HttpClient httpClient; private static final Logger LOG = LoggerFactory.getLogger(Execution.class); private final boolean invokeAsync; private final Semaphore semaphore; @@ -59,48 +60,44 @@ public class Execution implements Callable { } this.semaphore = new Semaphore(this.throttle); - this.asyncHttpClient = new AsyncHttpClient(new AsyncHttpClientConfig.Builder() - .setConnectTimeout(Integer.MAX_VALUE) - .setRequestTimeout(Integer.MAX_VALUE) - .setAllowPoolingConnections(true) - .build()); - + this.httpClient = HttpClient.newBuilder() + .authenticator(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(params.controllerAuthUsername, + params.controllerAuthPassword.toCharArray()); + } + }) + .build(); this.payloads = new ArrayList<>(); for (DestToPayload payload : payloads) { - AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.preparePost(payload.getDestination()) - .addHeader("Content-Type", "application/json") - .addHeader("Accept", "application/json") - .setBody(payload.getPayload()) - .setRequestTimeout(Integer.MAX_VALUE); - - requestBuilder.setRealm(new Realm.RealmBuilder() - .setScheme(Realm.AuthScheme.BASIC) - .setPrincipal(params.controllerAuthUsername) - .setPassword(params.controllerAuthPassword) - .setMethodName("POST") - .setUsePreemptiveAuth(true) - .build()); - - this.payloads.add(requestBuilder.build()); + HttpRequest request = HttpRequest.newBuilder(URI.create(payload.getDestination())) + .POST(BodyPublishers.ofString(payload.getPayload(), StandardCharsets.UTF_8)) + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .build(); + + this.payloads.add(request); } } private void invokeSync() { LOG.info("Begin sending sync requests"); - for (Request request : payloads) { + for (HttpRequest request : payloads) { try { - Response response = asyncHttpClient.executeRequest(request).get(); - if (response.getStatusCode() != 200 && response.getStatusCode() != 204) { - if (response.getStatusCode() == 409) { + HttpResponse response = httpClient.send(request, BodyHandlers.ofString()); + + if (response.statusCode() != 200 && response.statusCode() != 204) { + if (response.statusCode() == 409) { LOG.warn("Request failed, status code: {} - one or more of the devices" - + " is already configured, skipping the whole batch", response.getStatusCode()); + + " is already configured, skipping the whole batch", response.statusCode()); } else { - LOG.warn("Status code: {}", response.getStatusCode()); - LOG.warn("url: {}", request.getUrl()); - LOG.warn("body: {}", response.getResponseBody()); + LOG.warn("Status code: {}", response.statusCode()); + LOG.warn("url: {}", request.uri()); + LOG.warn("body: {}", response.body()); } } - } catch (InterruptedException | ExecutionException | IOException e) { + } catch (InterruptedException | IOException e) { LOG.warn("Failed to execute request", e); } } @@ -110,34 +107,23 @@ public class Execution implements Callable { private void invokeAsync() { LOG.info("Begin sending async requests"); - for (final Request request : payloads) { + for (final HttpRequest request : payloads) { try { semaphore.acquire(); } catch (InterruptedException e) { LOG.warn("Semaphore acquire interrupted"); } - asyncHttpClient.executeRequest(request, new AsyncCompletionHandler() { - @Override - public STATE onStatusReceived(final HttpResponseStatus status) throws Exception { - super.onStatusReceived(status); - if (status.getStatusCode() != 200 && status.getStatusCode() != 204) { - if (status.getStatusCode() == 409) { - LOG.warn("Request failed, status code: {} - one or more of the devices" - + " is already configured, skipping the whole batch", status.getStatusCode()); - } else { - LOG.warn("Request failed, status code: {}", - status.getStatusCode() + status.getStatusText()); - LOG.warn("request: {}", request.toString()); - } + httpClient.sendAsync(request, BodyHandlers.ofString()).whenComplete((response, error) -> { + if (response.statusCode() != 200 && response.statusCode() != 204) { + if (response.statusCode() == 409) { + LOG.warn("Request failed, status code: {} - one or more of the devices" + + " is already configured, skipping the whole batch", response.statusCode()); + } else { + LOG.warn("Request failed, status code: {}", response.statusCode()); + LOG.warn("request: {}", request); } - return STATE.CONTINUE; - } - - @Override - public Response onCompleted(final Response response) { - semaphore.release(); - return response; } + semaphore.release(); }); } LOG.info("Requests sent, waiting for responses"); diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/ScaleUtil.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/ScaleUtil.java index 5a65a5f887..ad8d49b651 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/ScaleUtil.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/ScaleUtil.java @@ -11,19 +11,18 @@ package org.opendaylight.netconf.test.tool; import ch.qos.logback.classic.Level; import com.google.common.base.Stopwatch; import com.google.common.io.CharStreams; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig.Builder; -import com.ning.http.client.Request; -import com.ning.http.client.Response; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.ConnectException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.util.List; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -190,36 +189,32 @@ public final class ScaleUtil { + "network-topology:network-topology/topology/topology-netconf/"; private static final Pattern PATTERN = Pattern.compile("connected"); - private final AsyncHttpClient asyncHttpClient = new AsyncHttpClient(new Builder() - .setConnectTimeout(Integer.MAX_VALUE) - .setRequestTimeout(Integer.MAX_VALUE) - .setAllowPoolingConnections(true) - .build()); + private final HttpClient httpClient = HttpClient.newBuilder().build(); private final NetconfDeviceSimulator simulator; private final int deviceCount; - private final Request request; + private final HttpRequest request; ScaleVerifyCallable(final NetconfDeviceSimulator simulator, final int deviceCount) { LOG.info("New callable created"); this.simulator = simulator; this.deviceCount = deviceCount; - AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.prepareGet(RESTCONF_URL) - .addHeader("content-type", "application/xml") - .addHeader("Accept", "application/xml") - .setRequestTimeout(Integer.MAX_VALUE); - request = requestBuilder.build(); + request = HttpRequest.newBuilder(URI.create(RESTCONF_URL)) + .GET() + .header("Content-Type", "application/xml") + .header("Accept", "application/xml") + .build(); } @Override public Void call() throws Exception { try { - final Response response = asyncHttpClient.executeRequest(request).get(); + final HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - if (response.getStatusCode() != 200 && response.getStatusCode() != 204) { - LOG.warn("Request failed, status code: {}", response.getStatusCode() + response.getStatusText()); + if (response.statusCode() != 200 && response.statusCode() != 204) { + LOG.warn("Request failed, status code: {}", response.statusCode()); EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS); } else { - final String body = response.getResponseBody(); + final String body = response.body(); final Matcher matcher = PATTERN.matcher(body); int count = 0; while (matcher.find()) { @@ -236,7 +231,7 @@ public final class ScaleUtil { SEMAPHORE.release(); } } - } catch (ConnectException | ExecutionException e) { + } catch (ConnectException e) { LOG.warn("Failed to connect to Restconf, is the controller running?", e); EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS); } diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/AsyncExecutionStrategy.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/AsyncExecutionStrategy.java index 1b23dcc475..1895e4c3bf 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/AsyncExecutionStrategy.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/AsyncExecutionStrategy.java @@ -5,16 +5,13 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.netconf.test.tool.client.http.perf; import static org.opendaylight.netconf.test.tool.client.http.perf.RequestMessageUtils.formRequest; -import com.ning.http.client.AsyncCompletionHandler; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.HttpResponseStatus; -import com.ning.http.client.Request; -import com.ning.http.client.Response; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; import java.util.concurrent.Semaphore; import org.opendaylight.netconf.test.tool.client.stress.ExecutionStrategy; import org.slf4j.Logger; @@ -24,15 +21,15 @@ public class AsyncExecutionStrategy implements ExecutionStrategy { private static final Logger LOG = LoggerFactory.getLogger(AsyncExecutionStrategy.class); + private final HttpClient httpClient; private final Parameters params; - private final AsyncHttpClient asyncHttpClient; private final Semaphore semaphore; RestPerfClient.RequestData payloads; - AsyncExecutionStrategy(final Parameters params, final AsyncHttpClient asyncHttpClient, - final RestPerfClient.RequestData payloads) { + AsyncExecutionStrategy(final Parameters params, final HttpClient httpClient, + final RestPerfClient.RequestData payloads) { this.params = params; - this.asyncHttpClient = asyncHttpClient; + this.httpClient = httpClient; this.payloads = payloads; this.semaphore = new Semaphore(RestPerfClient.throttle); } @@ -42,30 +39,25 @@ public class AsyncExecutionStrategy implements ExecutionStrategy { LOG.info("Begin sending async requests"); for (int i = 0; i < payloads.getRequests(); i++) { - String message = RequestMessageUtils.prepareMessage(payloads.getThreadId(), i, + final String message = RequestMessageUtils.prepareMessage(payloads.getThreadId(), i, payloads.getContentString(), payloads.getPort()); - Request request = formRequest(asyncHttpClient, payloads.getDestination(), params, message); + final String url = payloads.getDestination(); + final HttpRequest request = formRequest(url, message); try { semaphore.acquire(); } catch (InterruptedException e) { LOG.warn("Semaphore acquire interrupted"); } - asyncHttpClient.executeRequest(request, new AsyncCompletionHandler() { - @Override - public STATE onStatusReceived(HttpResponseStatus status) throws Exception { - super.onStatusReceived(status); - if (status.getStatusCode() != 200 && status.getStatusCode() != 204) { - LOG.warn("Request failed, status code: {}", status.getStatusCode() + status.getStatusText()); - LOG.warn("request: {}", request.toString()); - } - return STATE.CONTINUE; - } - - @Override - public Response onCompleted(Response response) { - semaphore.release(); - return response; + httpClient.sendAsync(request, BodyHandlers.ofString()).whenComplete((response, error) -> { + switch (response.statusCode()) { + case 200: + case 204: + break; + default: + LOG.warn("Request failed, status code: {}", response.statusCode()); + LOG.warn("request: {}", request); } + semaphore.release(); }); } LOG.info("Requests sent, waiting for responses"); diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/PerfClientCallable.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/PerfClientCallable.java index 2ec721f29e..02323933a5 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/PerfClientCallable.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/PerfClientCallable.java @@ -5,46 +5,38 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.netconf.test.tool.client.http.perf; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Builder; import java.util.concurrent.Callable; import org.opendaylight.netconf.test.tool.client.http.perf.RestPerfClient.RequestData; import org.opendaylight.netconf.test.tool.client.stress.ExecutionStrategy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class PerfClientCallable implements Callable { - private static final Logger LOG = LoggerFactory.getLogger(PerfClientCallable.class); - - private final Parameters params; - private final AsyncHttpClient asyncHttpClient; - private ExecutionStrategy executionStrategy; - private RequestData payloads; + private final ExecutionStrategy executionStrategy; - public PerfClientCallable(Parameters params, RequestData payloads) { - this.params = params; - this.payloads = payloads; - this.asyncHttpClient = new AsyncHttpClient(new AsyncHttpClientConfig.Builder() - .setConnectTimeout(Integer.MAX_VALUE) - .setRequestTimeout(Integer.MAX_VALUE) - .setAllowPoolingConnections(true) - .build()); - executionStrategy = getExecutionStrategy(); - } + public PerfClientCallable(final Parameters params, final RequestData payloads) { + final Builder builder = HttpClient.newBuilder(); + if (params.auth != null) { + builder.authenticator(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(params.auth.get(0), params.auth.get(1).toCharArray()); + } + }); + } - private ExecutionStrategy getExecutionStrategy() { - return params.async - ? new AsyncExecutionStrategy(params, asyncHttpClient, payloads) - : new SyncExecutionStrategy(params, asyncHttpClient, payloads); + this.executionStrategy = params.async + ? new AsyncExecutionStrategy(params, builder.build(), payloads) + : new SyncExecutionStrategy(builder.build(), payloads); } @Override public Void call() { executionStrategy.invoke(); - asyncHttpClient.closeAsynchronously(); return null; } } diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/RequestMessageUtils.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/RequestMessageUtils.java index 0b83cce0b5..2562e45698 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/RequestMessageUtils.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/RequestMessageUtils.java @@ -7,9 +7,10 @@ */ package org.opendaylight.netconf.test.tool.client.http.perf; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.Realm; -import com.ning.http.client.Request; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.nio.charset.StandardCharsets; import org.opendaylight.netconf.test.tool.TestToolUtils; public final class RequestMessageUtils { @@ -63,8 +64,8 @@ public final class RequestMessageUtils { return messageBuilder.toString(); } - public static RestPerfClient.RequestData formPayload(Parameters parameters, String editContentString, - int threadId, int requests) { + public static RestPerfClient.RequestData formPayload(final Parameters parameters, final String editContentString, + final int threadId, final int requests) { final int devicePort = parameters.sameDevice ? parameters.devicePortRangeStart : parameters.devicePortRangeStart + threadId; final StringBuilder destBuilder = new StringBuilder(DEST); @@ -90,23 +91,11 @@ public final class RequestMessageUtils { threadId, devicePort, requests); } - public static Request formRequest(AsyncHttpClient asyncHttpClient, String url, Parameters params, String msg) { - AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.preparePost(url) - .addHeader("content-type", "application/json") - .addHeader("Accept", "application/xml") - .setBody(msg) - .setRequestTimeout(Integer.MAX_VALUE); - - if (params.auth != null) { - requestBuilder.setRealm(new Realm.RealmBuilder() - .setScheme(Realm.AuthScheme.BASIC) - .setPrincipal(params.auth.get(0)) - .setPassword(params.auth.get(1)) - .setUsePreemptiveAuth(true) - .build()); - } - return requestBuilder.build(); + public static HttpRequest formRequest(final String url, final String msg) { + return HttpRequest.newBuilder(URI.create(url)) + .POST(BodyPublishers.ofString(msg, StandardCharsets.UTF_8)) + .header("content-type", "application/json") + .header("Accept", "application/xml") + .build(); } - - } diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/SyncExecutionStrategy.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/SyncExecutionStrategy.java index 79cb2450ee..94e3277963 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/SyncExecutionStrategy.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/SyncExecutionStrategy.java @@ -9,28 +9,22 @@ package org.opendaylight.netconf.test.tool.client.http.perf; import static org.opendaylight.netconf.test.tool.client.http.perf.RequestMessageUtils.formRequest; -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.Request; -import com.ning.http.client.Response; import java.io.IOException; -import java.util.concurrent.ExecutionException; +import java.net.http.HttpClient; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; import org.opendaylight.netconf.test.tool.client.stress.ExecutionStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - public class SyncExecutionStrategy implements ExecutionStrategy { - private static final Logger LOG = LoggerFactory.getLogger(SyncExecutionStrategy.class); - private final Parameters params; + private final HttpClient httpClient; private final RestPerfClient.RequestData payloads; - private final AsyncHttpClient asyncHttpClient; - SyncExecutionStrategy(final Parameters params, final AsyncHttpClient asyncHttpClient, - final RestPerfClient.RequestData payloads) { - this.params = params; - this.asyncHttpClient = asyncHttpClient; + SyncExecutionStrategy(final HttpClient httpClient, final RestPerfClient.RequestData payloads) { + this.httpClient = httpClient; this.payloads = payloads; } @@ -39,21 +33,22 @@ public class SyncExecutionStrategy implements ExecutionStrategy { LOG.info("Begin sending sync requests"); for (int i = 0; i < payloads.getRequests(); i++) { - String message = RequestMessageUtils.prepareMessage(payloads.getThreadId(), i, + final String message = RequestMessageUtils.prepareMessage(payloads.getThreadId(), i, payloads.getContentString(), payloads.getPort()); - Request request = formRequest(asyncHttpClient, payloads.getDestination(), params, message); + final HttpResponse response; try { - Response response = asyncHttpClient.executeRequest(request).get(); - if (response.getStatusCode() != 200 && response.getStatusCode() != 204) { - LOG.warn("Status code: {}", response.getStatusCode()); - LOG.warn("url: {}", request.getUrl()); - LOG.warn("body: {}", response.getResponseBody()); - } - } catch (InterruptedException | ExecutionException | IOException e) { + response = httpClient.send(formRequest(payloads.getDestination(), message), BodyHandlers.ofString()); + } catch (InterruptedException | IOException e) { LOG.warn("Failed to execute request", e); + return; + } + + if (response.statusCode() != 200 && response.statusCode() != 204) { + LOG.warn("Status code: {}", response.statusCode()); + LOG.warn("url: {}", response.uri()); + LOG.warn("body: {}", response.body()); } } LOG.info("End sending sync requests"); - } } -- 2.36.6