Undertown. Async responce with another thread - java

I try to realize how to build an true-async http server with Undertow. How to send a response asynchronously if I have another thread which one is processing a request already?
I wrote code like this:
Undertow server = Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(exchange -> {
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).thenAccept(string -> {
exchange.getResponseHeaders()
.put(Headers.CONTENT_TYPE, "text/plain");
exchange.getResponseSender().send("Hello World");
exchange.endExchange();
}).exceptionally(throwable -> {
System.out.println(throwable.toString());
return null;
});
}).build();
server.start();
but this server response 200 with no data and in logs
java.lang.IllegalStateException: UT000127: Response has already been sent
When I use io.undertow.server.HttpServerExchange#dispatch(java.lang.Runnable) method like this:
Undertow server = Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(exchange -> {
exchange.dispatch(() -> {
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).thenAccept(string -> {
exchange.getResponseHeaders()
.put(Headers.CONTENT_TYPE,"text/plain");
exchange.getResponseSender().send("Hello World");
exchange.endExchange();
}).exceptionally(throwable -> {
System.out.println(throwable.toString());
return null;
});
});
}).build();
server.start();
of course a response "Hello World" as expected but server creates new thread for every request!
(jvisualvm after 10 parallel requests)

If you call dispatch() without passing an executor, like this:
exchange.dispatch(() -> {
//...
});
it will dispatch it to the XNIO worker thread pool. It will not necessarily "create new thread for every request".
If you want to not dispatch it to another thread, you should do this:
exchange.dispatch(SameThreadExecutor.INSTANCE, () -> {
//...
});

the undertow not support this way,
i create a new project to solve it:
https://github.com/hank-whu/undertow-async
package io.undertow.async.pingpong;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import io.undertow.async.handler.AsyncHttpHandler;
import io.undertow.async.io.PooledByteBufferInputStream;
import io.undertow.async.io.PooledByteBufferOutputStream;
import io.undertow.connector.ByteBufferPool;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.StatusCodes;
public class PingPongAsyncHttpHandler extends AsyncHttpHandler {
#Override
protected void handleAsyncRequest(HttpServerExchange exchange, PooledByteBufferInputStream content)
throws Exception {
CompletableFuture//
.completedFuture(content)// init
.thenApplyAsync(this::readBytesAndClose)// read
.thenApplyAsync(bytes -> {// write
ByteBufferPool byteBufferPool = exchange.getConnection().getByteBufferPool();
PooledByteBufferOutputStream output = new PooledByteBufferOutputStream(byteBufferPool);
write(output, bytes);
return output;
})//
.thenAcceptAsync(output -> send(exchange, StatusCodes.OK, output));
}
private byte[] readBytesAndClose(PooledByteBufferInputStream content) {
try {
byte[] bytes = new byte[content.available()];
content.read(bytes);
return bytes;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {// must close it
content.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void write(PooledByteBufferOutputStream output, byte[] bytes) {
try {
output.write("asycn response: ");
output.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

Related

ExecutorService not shutting down after Tomcat shutdown

Thanks for this opportunity to ask questions here.
Summary
I have a spring boot application which runs on Tomcat. I'm subscribing ActiveMQ topic with StompClient. After connected i get messages via StompHandler's handleFrame method.
In that method i create MessageUtils which implements Runnable interface. In MessageUtils run method, creating new thread with ExecutorService and do the task generateMessage which send messages to kafka topic.
Problem
When I shutdown the tomcat, threads are still alive. In catalina.out;
A web application appears to have started a thread named [foo] but has failed to stop it. This is very likely to create a memory leak
So the tomcat cannot shutdown properly.
Code Samples
#Service
StompService.class
#EventListener(ApplicationReadyEvent.class)
public void start() {
logI("Service run Client Methods");
List<String> topics = Arrays.asList(topicListString.split(","));
for (String topic : topics) {
StompClient client = new StompClient(topic, username, password, url, topic, bootstrapAddress);
try {
client.run();
runMap.put(topic, client);
boolean connected = client.getSession().isConnected();
logI("Topic: " + topic + " is connected: " + connected);
} catch (InterruptedException e) {
logE("InterruptedException during start of stomp client: ", e);
} catch (TimeoutException e) {
logE("TimeoutException during start of stomp client: ", e);
} catch (ExecutionException e) {
logE("ExecutionException during start of stomp client: ", e);
} catch (Exception e) {
logE("Unexpected exception during start of stomp client: ", e);
}
}
}
StompClient.class
public void run() throws ExecutionException, TimeoutException, InterruptedException {
WebSocketClient client = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(client);
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
StompHeaders connectHeaders = new StompHeaders();
connectHeaders.add("login", this.userName);
connectHeaders.add("passcode", this.password);
stompClient.getDefaultHeartbeat();
stompClient.setTaskScheduler(taskScheduler);
stompClient.setMessageConverter(new StringMessageConverter());
stompClient.setAutoStartup(true);
StompSessionHandler sessionHandler = new StompHandler(this.topic, this.bootstrapAddress);
StompSession stompSession = null;
try {
stompSession = stompClient.connect(url, new WebSocketHttpHeaders(), connectHeaders, sessionHandler)
.get(5, TimeUnit.SECONDS);
} catch (Exception e) {
logE("Cannot connect with stomp client." , e);
}
this.setSession(stompSession);
}
StompHandler.class which extends StompSessionHandlerAdapter
#Override
public void handleFrame(StompHeaders headers, Object payload) {
String msg = (String) payload;
MessageUtils message = new MessageUtils();
message.setHeaders(headers);
message.setTopic(topic);
message.setMsg(msg);
message.setBootstrapAddress(bootstrapAddress);
message.run();
}
MessageUtils.class
#Override
public void run() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
generateMessage(getMsg().toString());
}
});
executorService.shutdown();
try {
executorService.awaitTermination(200, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
logI("InterruptedException during await termination", e);
}
}

How to send a message through all Threads?

At the moment i have a Server and a Client, and when the Client is connected to the Server, a Thread is created to handle all the resposnses from the respective Client and also to send any needed answers. My problem now is that i need to be able to send a message through every existent Thread to their respective Client.
I was thinking of doing it like this:
public class ServerThread extends Thread {
//ignore most of the constructor, just things i need
public ServerThread(Socket socket, int threadId, Manager manager) throws Exception {
try {
this.socket = socket;
this.threadId=threadId;
this.manager=manager;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
manager.addThread(); //This should add this Thread to the Collection in the Manager class
} catch (IOException ex) {
throw new Exception("Error", ex);
}
}
public void notify(String message){
// Do something
}
//In the end of the thread i would call manager.removeThread to remove the Thread from the Collection
}
public class Manager {
private //Thread Collection here
public Manager(){
//Initialize the collection;
}
public void addThread(){
//Add thread
}
public void removeThread(){
//Remove Thread
}
}
If this is a viable option to handle this, what Collection would i need to store the Threads and also, what would the notify(String message) method look like? It would need to call a method in Manager that would send a message to every Thread right?
If you want to create a multi-client server what is usually recommended is that in the main thread (or a separate thread) of the server class, the server will be accepting incoming Sockets (client) and with every socket accepted a new thread is created to service that client and it is better to have the service as a separate class that implements runnable or extends thread. Each service thread will be waiting for input from the client it is associated with and replying according to the client's request.
If you are looking to broadcast data to all the connected clients then what you need is to have an ArrayList that stores the client service objects and then loop over it, with every loop you send data to one of the connected clients but you have to make sure that you remove the clients that disconnected from the ArrayList otherwise it will start throwing exceptions.
usually, client service classes have the accepted socket, an input stream, and an output stream.
here is an example of a multiclient echo server that I have made maybe it will help.
public class TcpServer {
public TcpServer(){
ServerSocket server = null;
try{
server = new ServerSocket(9991);
while(!server.isClosed()){
Socket acceptedSocket = server.accept();
EchoService service = new EchoService(acceptedSocket);
service.start();
}
}catch (IOException e){
e.printStackTrace();
} finally {
if(server!=null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args){
new TcpServer();
}}
This is the service class:
public class EchoService extends Thread {
private Socket acceptedSocket;
private DataInputStream is;
private DataOutputStream os;
public EchoService(Socket acceptedSocket) {
try {
this.acceptedSocket = acceptedSocket;
is = new DataInputStream(acceptedSocket.getInputStream());
os = new DataOutputStream(acceptedSocket.getOutputStream());
} catch (IOException e) {
try {
if (this.acceptedSocket != null)
acceptedSocket.close();
if(is != null)
is.close();
if(os != null)
os.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
#Override
public void run() {
super.run();
try {
while (!acceptedSocket.isClosed()) {
String usrMsg = is.readUTF();
String serverMsg = "server: "+usrMsg;
os.writeUTF(serverMsg);
os.flush();
}
} catch (IOException e) {
try {
if(this.acceptedSocket != null)
acceptedSocket.close();
if(is != null)
is.close();
if(os != null)
os.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}}
This is the same example but with the Broadcast feature included
Server class:
package TCP;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class TcpServer {
public static ArrayList<EchoService> connectedServices;
public TcpServer(){
ServerSocket server = null;
try{
server = new ServerSocket(9991);
System.out.println("server started");
connectedServices = new ArrayList<>();
while(!server.isClosed()){
Socket acceptedSocket = server.accept();
System.out.println("client connected: "
+acceptedSocket.getInetAddress());
EchoService service = new EchoService(acceptedSocket);
service.start();
}
}catch (IOException e){
e.printStackTrace();
} finally {
if(server!=null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args){
new TcpServer();
}
public static void removeConnectedService(EchoService client) {
boolean removed = connectedServices.remove(client);
System.out.println("client has been removed"+
client.getAcceptedSocket().getInetAddress()+", "+removed);
}
public static void broadCastMsg(long id, String usrMsg) throws IOException {
for(EchoService client: connectedServices){
if(client.getId()!=id)
{
String serverMsg = "server broadcast: " + usrMsg;
client.getOs().writeUTF(serverMsg);
client.getOs().flush();
}
}
}
}
service class:
package TCP;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class EchoService extends Thread {
private Socket acceptedSocket;
private DataInputStream is;
private DataOutputStream os;
public EchoService(Socket acceptedSocket) {
try {
this.acceptedSocket = acceptedSocket;
is = new DataInputStream(acceptedSocket.getInputStream());
os = new DataOutputStream(acceptedSocket.getOutputStream());
} catch (IOException e) {
try {
if (this.acceptedSocket != null)
acceptedSocket.close();
if(is != null)
is.close();
if(os != null)
os.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
#Override
public void run() {
super.run();
try {
TcpServer.connectedServices.add(this);
while (!acceptedSocket.isClosed()) {
String usrMsg = is.readUTF();
if(usrMsg.contains("BROADCAST"))
TcpServer.broadCastMsg(this.getId(),usrMsg);
else {
String serverMsg = "server: " + usrMsg;
os.writeUTF(serverMsg);
os.flush();
}
}
} catch (IOException e) {
TcpServer.removeConnectedService(this);
try {
if(this.acceptedSocket != null)
acceptedSocket.close();
if(is != null)
is.close();
if(os != null)
os.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public DataInputStream getIs() {
return is;
}
public DataOutputStream getOs() {
return os;
}
public Socket getAcceptedSocket() {
return acceptedSocket;
}
}
Server output:
client 1 output:
client 2 output:
client 3 output:
I would create a static method getInstance(int threadId) in ServerThread.
Inside this, you create a syncronized and static Map (see class Collections).
In notify just navigate over the map and send your messages to your ServerThread instances.
(note: if it's a TreMap it will be sorted by the key)

FileNotFoundException when creating new Thread in java

I've an already works code to read an excel file. At first, the API in the controller clase receive the file as MultipartFile type. Then, because of some reasons, I need to convert the MultipartFile into File type. Here is the code:
private static File convert(MultipartFile file) throws IOException {
try {
File convertedFile = new File(file.getOriginalFilename());
convertedFile.createNewFile();
FileOutputStream fos = new FileOutputStream(convertedFile);
fos.write(file.getBytes());
fos.close();
return convertedFile;
} catch (IOException e) {
e.printStackTrace();
throw new IOException("Error in converting the file with error message: " + e.getMessage());
}
}
Here is the service class which called in the controller, which call the convert method above:
public void create(MultipartFile file) throws Exception {
try {
File newFile = convert(file);
// rest of code
} catch (Exception e) {
// rest of code
}
}
Before I try to call the service in a new thread, code above works fine. But, when I try to call the service in a new thread, like below code, it says java.io.FileNotFoundException (The system cannot find the file specified), and the main problem is in this line fos.write(file.getBytes());. Here is how I create the new Thread in the controller:
#RequestMapping(method = RequestMethod.POST, value = "uploadfile")
public ResponseEntity<?> create(#RequestParam (value = "file", required = false) MultipartFile file) throws Exception {
try {
// ...other process
// ================================== code below not work
Thread create;
create = new Thread() {
public void run() {
try {
service.create(file);
} catch (Exception e) {
e.printStackTrace();
}
}
};
create.start();
// ================================== code below not work
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
service.create(file);
} catch (Exception e) {
e.printStackTrace();
}
}
});
t1.start();
// ================================== code below not work
new Thread(() -> {
try {
service.create(file);
} catch (Exception e) {
e.printStackTrace();
}
}){{start();}};
// ...rest of code
} catch (Exception e) {
// ...rest of code
}
}
above is several way how I try to make the thread but none of them works, result with same execption.
Objective: In short, I want to make the file reading in background and immediately send response to client after the service called.
Spring supports async way by return a Callable object. the pseudo code is like:
#RequestMapping(method = RequestMethod.POST, value = "uploadfile")
public Callable<ResponseEntity<?>> create(#RequestParam (value = "file", required = false) MultipartFile file) throws Exception {
return () -> {
try {
service.create(file);
return ResponseEntity.ok()
} catch (Exception e) {
return ResponseEntity.error( /*some error*/
e.printStackTrace();
}
};
}
Here's a tutorial reference : https://niels.nu/blog/2016/spring-async-rest.html .
another reference: How to make a async rest with Spring?

Asterisk-Java shutdown after connect

I am using Asterisk with asterisk-java library to detect events. The follwoing code excerpt shows my usage. The connection should stay open, but it just closes after initializing when i am not using a endless loop. How can i keep the connection till the application closes?
[...]
public AsteriskManager(AsteriskAccountData asteriskAccountData)
throws ManagerCommunicationException {
this.asteriskAccountData = asteriskAccountData;
this.asteriskStateModel = new AsteriskStateModel();
new Thread(() -> {
asteriskServer = new DefaultAsteriskServer(
asteriskAccountData.getHost(),
asteriskAccountData.getUser(),
asteriskAccountData.getPassword()
);
ManagerConnectionFactory factory = new ManagerConnectionFactory(
asteriskAccountData.getHost(),
asteriskAccountData.getUser(),
asteriskAccountData.getPassword()
);
this.managerConnection = factory.createManagerConnection();
try {
asteriskServer.addAsteriskServerListener(this);
managerConnection.addEventListener(this);
registerListener();
//FIXME endless thread
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (ManagerCommunicationException ex) {
//FIXME error handling
}
}, "AsteriskManagerThread").start();
}
[...]

RxJava and rx.exceptions.MissingBackpressureException exception

I am trying to get a very basic RxJava based application to work. I have defined the following Observable class which reads and returns lines from a file:
public Observable<String> getObservable() throws IOException
{
return Observable.create(subscribe -> {
InputStream in = getClass().getResourceAsStream("/trial.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
try {
while((line = reader.readLine()) != null)
{
subscribe.onNext(line);
}
} catch (IOException e) {
subscribe.onError(e);
}
finally {
subscribe.onCompleted();
}
});
}
Next I have defined the subscrober code:
public static void main(String[] args) throws IOException, InterruptedException {
Thread thread = new Thread(() ->
{
RxObserver observer = new RxObserver();
try {
observer.getObservable()
.observeOn(Schedulers.io())
.subscribe( x ->System.out.println(x),
t -> System.out.println(t),
() -> System.out.println("Completed"));
} catch (IOException e) {
e.printStackTrace();
}
});
thread.start();
thread.join();
}
The file has close to 50000 records. When running the app I am getting "rx.exceptions.MissingBackpressureException". I have gone through some of the documentation and as suggested, I tried added the ".onBackpressureBuffer()" method in the call chain. But then I am not getting the exception but the completed call too isin't getting fired.
What is the right way to handle scenario wherein we have a fast producing Observable?
The first problem is that your readLine logic ignores backpressure. You can apply onBackpressureBuffer() just before observeOn to start with but there is a recent addition SyncOnSubscribe that let's you generate values one by one and takes care of backpressure:
SyncOnSubscribe.createSingleState(() => {
try {
InputStream in = getClass().getResourceAsStream("/trial.txt");
return new BufferedReader(new InputStreamReader(in));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
},
(s, o) -> {
try {
String line = s.readLine();
if (line == null) {
o.onCompleted();
} else {
o.onNext(line);
}
} catch (IOException ex) {
s.onError(ex);
}
},
s -> {
try {
s.close();
} catch (IOException ex) {
}
});
The second problem is that your Thread will complete way before all elements on the io thread has been delivered and thus the main program exits. Either remove the observeOn, add .toBlocking or use a CountDownLatch.
RxObserver observer = new RxObserver();
try {
CountDownLatch cdl = new CountDownLatch(1);
observer.getObservable()
.observeOn(Schedulers.io())
.subscribe( x ->System.out.println(x),
t -> { System.out.println(t); cdl.countDown(); },
() -> { System.out.println("Completed"); cdl.countDown(); });
cdl.await();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
The problem here is observeOn operator, since each Observer's onNext() call is scheduled to be called on a separate thread, your Observable keeps producing those scheduled calls in a loop regardless of subscriber (observeOn) capacity.
If you keep this synchronous, Observable will not emit next element until subscriber is done with the previous one, since it's all done on a one thread and you will not have backpressure problems anymore.
If you still want to use observeOn, you will have to implement backpressure logic in your Observable's OnSubscribe#call method

Categories