ClientThread.java
/*
* Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.jersey.grizzly2.httpserver.test.tools;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
/**
* Why this? To simulate parallel client access - several clients intensively sending requests
* and verifying responses.
*
* @author David Matejcek
*/
public abstract class ClientThread extends Thread {
/** Encryption used if you enable secured communication */
public static final String ENCRYPTION_PROTOCOL = "TLSv1.2";
private final ClientThreadSettings settings;
private final AtomicInteger counter;
private final AtomicReference<Throwable> error;
private volatile boolean stop;
public ClientThread(final ClientThreadSettings settings, final AtomicInteger counter,
final AtomicReference<Throwable> error) throws Exception {
this.settings = settings;
this.counter = counter;
this.error = error;
setName("client-" + settings.id);
setUncaughtExceptionHandler((t, e) -> {
stop = true;
error.compareAndSet(null, e);
});
}
/**
* @return {@link ClientThreadSettings}
*/
protected final ClientThreadSettings getSettings() {
return this.settings;
}
/**
* Executes the requests and checks the response.
*
* @throws Throwable
*/
protected abstract void doGetAndCheckResponse() throws Throwable;
/**
* If the client is stateful, override this method.
*/
protected void disconnect() {
// by default nothing to do.
}
/**
* Instructs the client thread to stop when possible.
*/
public void stopClient() {
this.stop = true;
}
@Override
public final void run() {
try {
// stop when asked to stop or any "brother" thread observed throwable
while (!stop && error.get() == null) {
doGetAndCheckResponse();
counter.incrementAndGet();
}
} catch (final Throwable t) {
throw new IllegalStateException("The client thread failed: " + getName(), t);
} finally {
disconnect();
}
}
/**
* @return Trivial {@link SSLContext}, accepting all certificates and host names
* @throws GeneralSecurityException
*/
protected static SSLContext createSslContext() throws GeneralSecurityException {
final SSLContext ctx = SSLContext.getInstance(ENCRYPTION_PROTOCOL);
ctx.init(null, new TrustManager[] {new NaiveTrustManager()}, null);
return ctx;
}
/**
* Simplified configuration of the client thread.
*/
public static class ClientThreadSettings {
/** Id of the client thread */
public final int id;
/** True if the connection will be encrypted */
public final boolean secured;
/** True if the protocol should be HTTP/2, false for HTTP 1.1 */
public final boolean useHttp2;
/** The endpoint {@link URI} of the servlet */
public final URI targetUri;
/**
* Creates simplified configuration of the client thread.
*
* @param id id of the client thread
* @param secured true if the connection will be encrypted
* @param useHttp2 true if the protocol should be HTTP/2, false for HTTP 1.1
* @param targetUri the endpoint {@link URI} of the servlet
*/
public ClientThreadSettings(final int id, final boolean secured, final boolean useHttp2, final URI targetUri) {
this.id = id;
this.secured = secured;
this.useHttp2 = useHttp2;
this.targetUri = targetUri;
}
}
}