Java create/overwrite http server - java

I'm creating a plugin on a certain platform (the details are irrelevant) and need to create a HTTP endpoint. In normal circumstances you'd create a http server and stop it whenever you're done using it or when the application stops, however, in my case I can't detect when the plugin is being uninstalled/reinstalled.
The problem
When someone installs my plugin twice, the second time it will throw an error because I'm trying to create a http server on a port which is already in use. Since it's being reinstalled, I can't save the http server on some static variable either. In other words, I need to be able to stop a previously created http server without having any reference to it.
My attempt
I figured the only way to interact with the original reference to the http server would be to create a thread whenever the http server starts, and then overwrite the interrupt() method to stop the server, but somehow I'm still receiving the 'port is already in use' error. I'm using Undertow as my http server library, but this problem applies to any http server implementation.
import io.undertow.Undertow;
import io.undertow.util.Headers;
public class SomeServlet extends Thread {
private static final String THREAD_NAME = "some-servlet-container-5391301";
private static final int PORT = 5839;
private Undertow server;
public static void listen() { // this method is called whenever my plugin is installed
deleteExistingServer();
new SomeServlet().start();
}
private static void deleteExistingServer() {
for (Thread t : Thread.getAllStackTraces().keySet()) {
if (t.getName().equals(THREAD_NAME)) {
t.interrupt();
}
}
}
#Override
public void run() {
createServer();
}
#Override
public void interrupt() {
try {
System.out.println("INTERRUPT");
this.server.stop();
} finally {
super.interrupt();
}
}
private void createServer() {
this.server = Undertow.builder()
.addHttpListener(PORT, "localhost")
.setHandler(exchange -> {
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
exchange.getResponseSender().send("Hello World!");
})
.build();
this.server.start();
}
}
Desired behaviour
Whenever listen() is called, it will remove any previously existing http server and create a new one, without relying on storing the server on a static variable.

You could try com.sun.net.httpserver.HttpServer. Use http://localhost:8765/stop to stop and 'http://localhost:8765/test' for test request:
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
public class TestHttpServer {
public static void main(String[] args) throws IOException {
final HttpServer server = HttpServer.create();
server.bind(new InetSocketAddress(8765), 0);
server.createContext("/test", httpExchange -> {
String response = "<html>TEST!!!</html>";
httpExchange.sendResponseHeaders(200, response.length());
OutputStream os = httpExchange.getResponseBody();
os.write(response.getBytes());
os.close();
});
server.createContext("/stop", httpExchange -> server.stop(1));
server.start();
}
}

Related

Sending a GET http request freezes my window [duplicate]

I'm fairly new to Java, so this may seem obvious to some. I've worked a lot with ActionScript, which is very much event based and I love that. I recently tried to write a small bit of Java code that does a POST request, but I've been faced with the problem that it's a synchronous request, so the code execution waits for the request to complete, time out or present an error.
How can I create an asynchronous request, where the code continues the execution and a callback is invoked when the HTTP request is complete? I've glanced at threads, but I'm thinking it's overkill.
If you are in a JEE7 environment, you must have a decent implementation of JAXRS hanging around, which would allow you to easily make asynchronous HTTP request using its client API.
This would looks like this:
public class Main {
public static Future<Response> getAsyncHttp(final String url) {
return ClientBuilder.newClient().target(url).request().async().get();
}
public static void main(String ...args) throws InterruptedException, ExecutionException {
Future<Response> response = getAsyncHttp("http://www.nofrag.com");
while (!response.isDone()) {
System.out.println("Still waiting...");
Thread.sleep(10);
}
System.out.println(response.get().readEntity(String.class));
}
}
Of course, this is just using futures. If you are OK with using some more libraries, you could take a look at RxJava, the code would then look like:
public static void main(String... args) {
final String url = "http://www.nofrag.com";
rx.Observable.from(ClientBuilder.newClient().target(url).request().async().get(String.class), Schedulers
.newThread())
.subscribe(
next -> System.out.println(next),
error -> System.err.println(error),
() -> System.out.println("Stream ended.")
);
System.out.println("Async proof");
}
And last but not least, if you want to reuse your async call, you might want to take a look at Hystrix, which - in addition to a bazillion super cool other stuff - would allow you to write something like this:
For example:
public class AsyncGetCommand extends HystrixCommand<String> {
private final String url;
public AsyncGetCommand(final String url) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HTTP"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionIsolationThreadTimeoutInMilliseconds(5000)));
this.url = url;
}
#Override
protected String run() throws Exception {
return ClientBuilder.newClient().target(url).request().get(String.class);
}
}
Calling this command would look like:
public static void main(String ...args) {
new AsyncGetCommand("http://www.nofrag.com").observe().subscribe(
next -> System.out.println(next),
error -> System.err.println(error),
() -> System.out.println("Stream ended.")
);
System.out.println("Async proof");
}
PS: I know the thread is old, but it felt wrong that no ones mentions the Rx/Hystrix way in the up-voted answers.
You may also want to look at Async Http Client.
Note that java11 now offers a new HTTP api HttpClient, which supports fully asynchronous operation, using java's CompletableFuture.
It also supports a synchronous version, with calls like send, which is synchronous, and sendAsync, which is asynchronous.
Example of an async request (taken from the apidoc):
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/"))
.timeout(Duration.ofMinutes(2))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofFile(Paths.get("file.json")))
.build();
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
Based on a link to Apache HTTP Components on this SO thread, I came across the Fluent facade API for HTTP Components. An example there shows how to set up a queue of asynchronous HTTP requests (and get notified of their completion/failure/cancellation). In my case, I didn't need a queue, just one async request at a time.
Here's where I ended up (also using URIBuilder from HTTP Components, example here).
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.http.client.fluent.Async;
import org.apache.http.client.fluent.Content;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.concurrent.FutureCallback;
//...
URIBuilder builder = new URIBuilder();
builder.setScheme("http").setHost("myhost.com").setPath("/folder")
.setParameter("query0", "val0")
.setParameter("query1", "val1")
...;
URI requestURL = null;
try {
requestURL = builder.build();
} catch (URISyntaxException use) {}
ExecutorService threadpool = Executors.newFixedThreadPool(2);
Async async = Async.newInstance().use(threadpool);
final Request request = Request.Get(requestURL);
Future<Content> future = async.execute(request, new FutureCallback<Content>() {
public void failed (final Exception e) {
System.out.println(e.getMessage() +": "+ request);
}
public void completed (final Content content) {
System.out.println("Request completed: "+ request);
System.out.println("Response:\n"+ content.asString());
}
public void cancelled () {}
});
You may want to take a look at this question: Asynchronous IO in Java?
It looks like your best bet, if you don't want to wrangle the threads yourself is a framework. The previous post mentions
Grizzly, https://grizzly.dev.java.net/, and Netty, http://www.jboss.org/netty/.
From the netty docs:
The Netty project is an effort to provide an asynchronous event-driven network application framework and tools for rapid development of maintainable high performance & high scalability protocol servers & clients.
Apache HttpComponents also have an async http client now too:
/**
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.0-beta4</version>
</dependency>
**/
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.concurrent.Future;
import org.apache.http.HttpResponse;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.client.methods.AsyncCharConsumer;
import org.apache.http.nio.client.methods.HttpAsyncMethods;
import org.apache.http.protocol.HttpContext;
public class HttpTest {
public static void main(final String[] args) throws Exception {
final CloseableHttpAsyncClient httpclient = HttpAsyncClients
.createDefault();
httpclient.start();
try {
final Future<Boolean> future = httpclient.execute(
HttpAsyncMethods.createGet("http://www.google.com/"),
new MyResponseConsumer(), null);
final Boolean result = future.get();
if (result != null && result.booleanValue()) {
System.out.println("Request successfully executed");
} else {
System.out.println("Request failed");
}
System.out.println("Shutting down");
} finally {
httpclient.close();
}
System.out.println("Done");
}
static class MyResponseConsumer extends AsyncCharConsumer<Boolean> {
#Override
protected void onResponseReceived(final HttpResponse response) {
}
#Override
protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl)
throws IOException {
while (buf.hasRemaining()) {
System.out.print(buf.get());
}
}
#Override
protected void releaseResources() {
}
#Override
protected Boolean buildResult(final HttpContext context) {
return Boolean.TRUE;
}
}
}
It has to be made clear the HTTP protocol is synchronous and this has nothing to do with the programming language. Client sends a request and gets a synchronous response.
If you want to an asynchronous behavior over HTTP, this has to be built over HTTP (I don't know anything about ActionScript but I suppose that this is what the ActionScript does too). There are many libraries that could give you such functionality (e.g. Jersey SSE). Note that they do somehow define dependencies between the client and the server as they do have to agree on the exact non standard communication method above HTTP.
If you cannot control both the client and the server or if you don't want to have dependencies between them, the most common approach of implementing asynchronous (e.g. event based) communication over HTTP is using the webhooks approach (you can check this for an example implementation in java).
Hope I helped!
Here is a solution using apache HttpClient and making the call in a separate thread. This solution is useful if you are only making one async call. If you are making multiple calls I suggest using apache HttpAsyncClient and placing the calls in a thread pool.
import java.lang.Thread;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
public class ApacheHttpClientExample {
public static void main(final String[] args) throws Exception {
try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
final HttpGet httpget = new HttpGet("http://httpbin.org/get");
new Thread(() -> {
final String responseBody = httpclient.execute(httpget);
}).start();
}
}
}

Java HttpsServer multi threaded

I have set up an HttpsServer in Java. All of my communication works perfectly. I set up multiple contexts, load a self-signed certificate, and even start up based on an external configuration file.
My problem now is getting multiple clients to be able to hit my secure server. To do so, I would like to somehow multi-thread the requests that come in from the HttpsServer but cannot figure out how to do so. Below is my basic HttpsConfiguration.
HttpsServer server = HttpsServer.create(new InetSocketAddress(secureConnection.getPort()), 0);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(secureConnection.getKeyManager().getKeyManagers(), secureConnection.getTrustManager().getTrustManagers(), null);
server.setHttpsConfigurator(new SecureServerConfiguration(sslContext));
server.createContext("/", new RootHandler());
server.createContext("/test", new TestHandler());
server.setExecutor(Executors.newCachedThreadPool());
server.start();
Where secureConnection is a custom class containing server setup and certificate information.
I attempted to set the executor to Executors.newCachedThreadPool() and a couple of other ones. However, they all produced the same result. Each managed the threads differently but the first request had to finish before the second could process.
I also tried writing my own Executor
public class AsyncExecutor extends ThreadPoolExecutor implements Executor
{
public static Executor create()
{
return new AsyncExecutor();
}
public AsyncExecutor()
{
super(5, 10, 10000, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(12));
}
#Override
public void execute(Runnable process)
{
System.out.println("New Process");
Thread newProcess = new Thread(process);
newProcess.setDaemon(false);
newProcess.start();
System.out.println("Thread created");
}
}
Unfortunately, with the same result as the other Executors.
To test I am using Postman to hit the /Test endpoint which is simulating a long running task by doing a Thread.sleep(10000). While that is running, I am using my Chrome browser to hit the root endpoint. The root page does not load until the 10 second sleep is over.
Any thoughts on how to handle multiple concurrent requests to the HTTPS server?
For ease of testing, I replicated my scenario using the standard HttpServer and condensed everything into a single java program.
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class Example
{
private final static int PORT = 80;
private final static int BACKLOG = 10;
/**
* To test hit:
* <p><b>http://localhost/test</b></p>
* <p>This will hit the endoint with the thread sleep<br>
* Then hit:</p>
* <p><b>http://localhost</b></p>
* <p>I would expect this to come back right away. However, it does not come back until the
* first request finishes. This can be tested with only a basic browser.</p>
* #param args
* #throws Exception
*/
public static void main(String[] args) throws Exception
{
new Example().start();
}
private void start() throws Exception
{
HttpServer server = HttpServer.create(new InetSocketAddress(PORT), BACKLOG);
server.createContext("/", new RootHandler());
server.createContext("/test", new TestHandler());
server.setExecutor(Executors.newCachedThreadPool());
server.start();
System.out.println("Server Started on " + PORT);
}
class RootHandler implements HttpHandler
{
#Override
public void handle(HttpExchange httpExchange) throws IOException
{
String body = "<html>Hello World</html>";
httpExchange.sendResponseHeaders(200, body.length());
OutputStream outputStream = httpExchange.getResponseBody();
outputStream.write(body.getBytes("UTF-8"));
outputStream.close();
}
}
class TestHandler implements HttpHandler
{
#Override
public void handle(HttpExchange httpExchange) throws IOException
{
try
{
Thread.sleep(10000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
String body = "<html>Test Handled</html>";
httpExchange.sendResponseHeaders(200, body.length());
OutputStream outputStream = httpExchange.getResponseBody();
outputStream.write(body.getBytes("UTF-8"));
outputStream.close();
}
}
}
TL;DR: It's OK, just use two different browsers, or specialized tool to test it.
You original implementation is OK and it work as expected, no custom Executor needed. For each request it executes method of "shared" handler class instance. It always picks up free thread from pool, so each method call is executed in different thread.
The problem seems to be, that when you use multiple windows of the same browser to test this behavior... for some reason requests get executed in serialised way (only one at the time). Tested with latest Firefox, Chrome, Edge and Postman. Edge and Postman work as expected. Also anonymous mode of Firefox and Chrome helps.
Same local URL opened at the same time from two Chrome windows. In first the page loaded after 5s, I got Thread.sleep(5000) so that's OK. Second window loaded respons in 8,71s, so there is 3,71s delay of unknown origin.
My guess? Probably some browser internal optimization or failsafe mechanism.
Try specifying a non-zero maximum backlog (the second argument to create()):
HttpsServer server = HttpsServer.create(new InetSocketAddress(secureConnection.getPort()), 10);
I did some experiments and what works for me is:
public void handler(HttpExchange exchange) {
executor.submit(new SomeOtherHandler());
}
public class SomeOtherHandler implements Runnable {
}
where the executor is the one you created as thread pool.

httpClient connection not closing

Version
vert.x core: 3.3.0
Context
Am just trying to run http client in core examples io.vertx.example.core.http.simple.Client.
While running this example its found that the established connection not closing after completion of request.
Server side I didnt see any issue. Since while trying with jmeter and server its working fine. So I think that the problem is in the HttpClient.
Anyone can help me on this?
Thanks in advance.
Steps to reproduce
running io.vertx.example.core.http.simple.Server code
running io.vertx.example.core.http.simple.Client code
Extra
The following shown even after the request and response is ended. while giving
LINUX
lsof -i -P
java 32551 USER 223u IPv4 16264097 0t0 TCP localhost:8080->localhost:26980 (ESTABLISHED)
java 32634 USER 218u IPv4 16264087 0t0 TCP localhost:26980->localhost:8080 (ESTABLISHED)
WINDOWS
TCP 127.0.0.1:8080 FSSCHND12957:56893 ESTABLISHED
TCP 127.0.0.1:56893 FSSCHND12957:8080 ESTABLISHED
Tried in both LINUX and WINDOWS system.
Client Code
package io.vertx.example.core.http.simple;
import io.vertx.core.AbstractVerticle;
import io.vertx.example.util.Runner;
/*
#author Tim Fox
*/
public class Client extends AbstractVerticle {
// Convenience method so you can run it in your IDE
public static void main(String[] args) {
Runner.runExample(Client.class);
}
#Override
public void start() throws Exception {
vertx.createHttpClient().getNow(8080, "localhost", "/", resp -> {
System.out.println("Got response " + resp.statusCode());
resp.bodyHandler(body -> {
System.out.println("Got data " + body.toString("ISO-8859-1"));
});
});
}
}
Server Code
package io.vertx.example.core.http.simple;
import io.vertx.core.AbstractVerticle;
import io.vertx.example.util.Runner;
/*
#author Tim Fox
*/
public class Server extends AbstractVerticle {
// Convenience method so you can run it in your IDE
public static void main(String[] args) {
Runner.runExample(Server.class);
}
#Override
public void start() throws Exception {
vertx.createHttpServer().requestHandler(req -> {
req.response().putHeader("content-type", "text/html").end("
Hello from vert.x!
");
}).listen(8080);
}
}
We have to close the httpClient which we normally do in java. Only end() is not closing the connection. httpClient.close() is required.... This solved my issue..
Modified code:
public class Client extends AbstractVerticle {
// Convenience method so you can run it in your IDE
public static void main(String[] args) {
Runner.runExample(Client.class);
}
#Override
public void start() throws Exception {
HttpClient httpClient = vertx.createHttpClient().getNow(8080, "localhost", "/", resp -> {
System.out.println("Got response " + resp.statusCode());
resp.bodyHandler(body -> {
System.out.println("Got data " + body.toString("ISO-8859-1"));
httpClient.close();
});
});
}
}

[Californium/CoAP/LWM2M]: Reusing message send endpoint for server not possible?

I am building a tool that can send CoAP messages to another peer (different implementation), but I am having difficulties. I am using the CoAP library called "Californium" and am developing the tool in java/eclipse. Here's the deal: I send a message over californium's "default endpoint", which allows the system to make up a source-port for the UDP "connection". I want to listen on this same source-port using californium's Server object, but I am getting the following error:
SEVERE: Could not start endpoint
java.net.BindException: Address already in use
So my question is: how do I first send a CoAP message and start listening for other CoAP messages on the same socket using Californium?
Below is the java code for the client. What it does is "register" using a certain protocol layered on top of CoAP. After registering I want it to re-use the UDP socket for listening for subsequent messages of the entity I registered with earlier.
NOTE: The server part of the client works when I explicitly tell it to listen to a certain port (e.g. 5683), leave out the register part and test it with the Firefox Addon "Copper" (i.e. Copper can get to the /1 /1/1 /1/1/0 resources).
package com.example.l2mwm.client;
import java.net.InetSocketAddress;
import ch.ethz.inf.vs.californium.coap.CoAP.Code;
import ch.ethz.inf.vs.californium.coap.CoAP.ResponseCode;
import ch.ethz.inf.vs.californium.coap.CoAP;
import ch.ethz.inf.vs.californium.coap.Request;
import ch.ethz.inf.vs.californium.coap.Response;
import ch.ethz.inf.vs.californium.network.Endpoint;
import ch.ethz.inf.vs.californium.network.EndpointManager;
import ch.ethz.inf.vs.californium.server.Server;
import ch.ethz.inf.vs.californium.server.resources.CoapExchange;
import ch.ethz.inf.vs.californium.server.resources.Resource;
import ch.ethz.inf.vs.californium.server.resources.ResourceBase;
public class Main {
public static void main(String[] args) {
Endpoint endpoint;
if ((endpoint = register()) != null) {
listen(endpoint);
} else {
System.out.println("Couldn't register!");
}
}
private static void listen(Endpoint endpoint) {
InetSocketAddress sockAddress = endpoint.getAddress();
int port = sockAddress.getPort();
Server server = new Server(port);
Resource topResource = new ResourceBase("1") {
#Override
public void handleGET(CoapExchange exchange) {
exchange.respond(ResponseCode.CONTENT, "this is /1's value!");
}
#Override
public String getPath() {
return "/";
}
};
Resource instanceResource = new ResourceBase("1") {
#Override
public void handleGET(CoapExchange exchange) {
exchange.respond(ResponseCode.CONTENT, "this is /1/1's value!");
}
#Override
public String getPath() {
return "/1/";
}
};
topResource.add(instanceResource);
instanceResource.add(new ResourceBase("0") {
#Override
public void handleGET(CoapExchange exchange) {
exchange.respond(ResponseCode.CONTENT, "this is /1/1/0's value!");
}
#Override
public String getPath() {
return "/1/1/";
}
});
server.add(topResource);
server.start();
}
private static Endpoint register() {
Request request = new Request(Code.POST);
request.setURI("localhost:5684/rd?ep=coapclient&lt=86400&b=U");
request.setPayload("</1/1/0>");
Endpoint endpoint = EndpointManager.getEndpointManager().getDefaultEndpoint();
request.send(endpoint);
Response response;
ResponseCode responseCode = null;
try {
response = request.waitForResponse();
} catch (InterruptedException e) {
System.out.println(e.getMessage());
return null;
}
responseCode = response.getCode();
if (responseCode != CoAP.ResponseCode.CREATED) {
return null;
}
return endpoint;
}
}
You need to first bind your UDP socket and then start your LWM2M register.
Because what you do: create CoAP Endpoint (bind a udp server) and than you bind again in your listen method.
// list to the UDP post 5555
coapServer = new Server();
Endpoint endpoint = new CoAPEndpoint(new InetSocketAddress("localhost",5555);
coapServer.addEndpoint(endpoint);
// send a message to a LWM2M server:
request request = new Request(Code.POST);
request.setURI("iot.eclipse.org:5683/rd?ep=coapclient&lt=86400&b=U");
request.setPayload("</1/1/0>");
Endpoint endpoint = EndpointManager.getEndpointManager().getDefaultEndpoint();
request.send(endpoint);
You can still access to your client using copper on coap://localhost:5555

Custom gRPC command for Dropwizard exits immediately

I've created an application using Dropwizard that starts a gRPC server. I do not use the regular server, and want to start my application using java -jar my-fat.jar grpc config.yml instead.
I've come as far as to add the command as the only available command during startup by overriding the corresponding method in the application class:
public class App extends Application<Configuration> {
public static void main(String[] args) throws Exception {
new App().run(args);
}
#Override
protected void addDefaultCommands(final Bootstrap<Configuration> bootstrap) {
bootstrap.addCommand(new GrpcCommand(this));
}
}
I can launch my application using java -jar my-fat.jar grpc config.yml. My command looks like this:
public class GrpcCommand extends EnvironmentCommand<Configuration> {
public GrpcCommand(Application<Configuration> application) {
this(application, "grpc", "Runs the Dropwizard application as a gRPC server");
}
/**
* Creates a new environment command.
*
* #param application the application providing this command
* #param name the name of the command, used for command line invocation
* #param description a description of the command's purpose
*/
protected GrpcCommand(final Application<Configuration> application, final String name, final String description) {
super(application, name, description);
}
#Override
protected void run(final Environment environment, final Namespace namespace, final Configuration configuration) throws Exception {
final var certificateService = AzureCertificateService.createWithClients(
AzureSecretClient.create(configuration.getKeyVaultConfiguration()),
AzureCertificateClient.create(configuration.getKeyVaultConfiguration())
);
final var validationService = CertificateValidationService.create(certificateService);
final var signingService = CertificateSigningService.create(certificateService);
final Pair<X509Certificate, KeyPair> certificate = certificateService.getSigningCertificateWithKeyPair();
final BaseApiImpl baseApi = new BaseApiImpl(validationService, signingService);
final GrpcServer grpcServer = GrpcServer.newBuilder()
.withBaseApi(baseApi)
.withConfiguration(configuration.getGrpcConfiguration())
.withCertificate(certificate.getLeft())
.withPrivateKey(certificate.getRight().getPrivate())
.build();
new Thread(() -> {
try {
grpcServer.start();
} catch (Exception e) {
e.printStackTrace();
}
}).run();
environment.healthChecks().register("grpc-server", new GrpcServerHealthCheck(grpcServer));
}
}
The way that thread is started is not for production use, I'm just trying to get forward. The start method for the GrpcServer class:
#Override
public void start() throws Exception {
final NettyServerBuilder nettyServerBuilder = NettyServerBuilder.forPort(configuration.getPort())
.addService(baseApi)
.intercept(new OriginInterceptor());
if (certificate != null && privateKey != null) {
LOG.info("Got certificate and private key, enabling SSL");
nettyServerBuilder.sslContext(buildSslContext());
}
server = nettyServerBuilder
.build()
.start();
LOG.info("Server started at port {}", server.getPort());
}
And I see the message GrpcServer: Server started at port 50441 in my log when I start. However, the application does not stay open. What am I missing? Shouldn't my use of the thread create a thread that stops the application from exiting? How can I keep the application running after the gRPC server has started?
When I disabled the server command, Jetty isn't started either (of course), which kept the application alive previously.
I found the simplest solution in the gRPC Hello World Example.
My start method now looks like this:
public void start() throws Exception {
// Everything else as above
Runtime.getRuntime().addShutdownHook(new Thread(GrpcServer.this::stop));
LOG.info("Server started at port {}", server.getPort());
blockUntilShutdown();
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}

Categories