Is there an option for tomcat to return some response to a request whilst at the same time executing a method in a backend service?
In my use case, I have a query which takes ~60s to execute. I'd like to respond a string to the request to let the user know that it will take some time whilst still executing the query itself in the backend service.
So instead of something like this:
...
response = this.service.getSomeData(request);
return response;
I'd like to do this:
...
return "Data not found"
this.service.getSomeData(request); // actually stores the result in a s3 bucket
You can execute it using another thread:
class MyServiceExecution implements Runnable{
public void run(){
System.out.println("thread is running...");
}
public static void main(String args[]){
MyServiceExecution m1=new MyServiceExecution();
Thread t1 =new Thread(m1);
t1.start();
}
}
Be careful when you implement threads... Read some about performance, garbage collection, etc.
Related
It was my first post on the community. Any comments and suggestions are welcome.
I have a web service on end point http://ip:port/report. This service execute a Runnable class. After executing the Runnable, is it possible to check / monitor the thread with a separate service call?
I am still working and searching for some stuff like ExecutorService, but still no luck. Is there any other way to do this? Please suggest some probable solution that I can check. Sorry for my grammar. I hope I can explain it clear with my sample code below.
My code is something like this.
Running the thread on : http://ip:port/report
public String runReport {
// Run the the runnable
Report report = new Report();
String threadName = "REPORT1";
Thread t = new Thread(report, threadName);
t.start();
// return thread some details
return t.getId() + "|" + t.hashCode();
}
My runnable class
public class Report {
private String status;
#Override
public void run() {
//Update status
setStatus("Running");
//... do stuff
//Update status
setStatus("End")
}
// Getter and Setter
}
My checker class on http://ip:port/report/check/some_param
public String check( int threadId ) {
// Search the thread by threadId and check the current status
//Report.getStatus();
}
Using thread IDs may not be the best idea, especially because you're likely to use a pool that reuses threads.
A simple solution is to generate IDs for your jobs and maintain a map that you can use to read the status.
As an example, you can use unique IDs for your task IDs:
Map<String, Report> jobs = new ConcurrentHashMap<>();
ExecutorService executorService = Executors.newFixedThreadPool(10); //just an example
public String runReport {
// Run the the runnable
Report report = new Report();
//For a numeric sequence, you can use something like AtomicLong
String jobId = UUID.randomUUID().toString();
jobs.put(jobId, report);
//using a thread pool may be a better idea.
executorService.submit(report);
// return the job ID
return jobId;
}
And to check the status, you just read the map:
public String check(String jobId) {
return jobs.get(jobId).getStatus(); //remember null checks
}
You would then just need to know when to remove entries from the map based on how you expect the check method to be called.
Maintain the map in the class of your job id.
Whenever that thread is initialized, pass the id in the constructor and when it starts processing put the status as running and when it gets completed, just before ending execution in run method, put the status as end. Something like below
public void run(){
try{
jobsMap.put(this.jobId, "Running");
// your business logic here
jobsMap.put(this.jobId,"Completed");
} catch(Exception e){
jobsMap.put(this.jobId,"Failed. Reason -"+e.getMessage);
// exception handling
}
}
There is a method foo() in controller, which have to wait another method bar() triggered to continue execution.
#GetMapping("/foo")
public void foo(){
doSomething();
// wait until method bar() triggered
doAnotherSomething();
}
#GetMapping("/bar")
public void bar(){
// make foo() continue execute after being called
}
My solution is: saving a status flag in database/cache, while foo() is waiting, the thread loops searching if the status changed.
However, this solution will blocke request thread for seconds.
Is there any way to make foo() method run asynchronously, thus won't block thread execution?
This question is too broad. Yes you can use DeferredResult to finish a web request later. But doAnotherSomething() should actually do stuff asynchronously, otherwise you still end up using a thread, just not the one from the app server's pool. Which would be a waste since you can simply increase the app server's pool size and be done with it. "Offloading" work from it to another pool is a wild goose chase.
You achieve truly asynchronous execution when you wait on more than one action in a single thread. For example by using asynchronous file or socket channels you can read from multiple files/sockets at once. If you're using a database, the database driver must support asynchronous execution.
Here's an example of how to use the mongodb async driver:
#GetMapping("/foo")
public DeferredResult<ResponseEntity<?>> foo() {
DeferredResult<ResponseEntity<?>> res = new DeferredResult<>();
doSomething();
doAnotherSomething(res);
return res;
}
void doAnotherSomething(DeferredResult<ResponseEntity<?>> res) {
collection.find().first(new SingleResultCallback<Document>() {
public void onResult(final Document document, final Throwable t) {
// process (document)
res.setResult(ResponseEntity.ok("OK")); // finish the request
}
});
}
You can use CountDownLatch to wait till the dependent method is executed. For the sake of simplicity, I have used a static property. Make sure both methods have access to the same CountDownLatch object. ThreadLocal<CountDownLatch> could also be considered for this usecase.
private static CountDownLatch latch = new CountDownLatch(1);
#GetMapping("/foo")
public void foo(){
doSomething();
// wait until method bar() triggered
latch.await();
doAnotherSomething();
}
#GetMapping("/bar")
public void bar(){
// make foo() continue execute after being called
latch.countDown();
}
i have a spring boot aplication and i want send email with javamail using ses on aws. but if I send an email, while it and sent no other process is executed.
I want to send the email through a thread, but I've implemented a thread in this way and even then the email sending process is not asynchronous.
when I make this request to send email and then list all to see how the processing is, as long as the sending of the email does not finish the list request is not executed
#GetMapping
public ResponseEntity<?> listarUsuarios(){
System.out.println("--------begin send mail------------");
new SendMail(emailService).run();
System.out.println("--------finish send mail------------");
List<Usuario> usuariosList = usuarioRepository.findAll(); // <- this process not is processed when send email not finish
return new ResponseEntity<>(usuariosList,HttpStatus.OK);
}
.
public class SendMail extends Thread {
public EmailService emailService;
public SendMail(EmailService emailService) {
this.emailService = emailService;
}
public void run(){
try {
emailService.EnviarEmailDeConfirmacao("daviresio#gmail.com", 1, "Subject test mail","body test mail");
} catch (Exception e) {
e.printStackTrace();
}
}
}
You are not starting a new thread. Instead, you are calling the run() method directly:
new SendMail(emailService).run();
Call start() instead to start a new thread:
new SendMail(emailService).start();
By the way, starting new threads like this from a web application is bad practice. It's better to use for example an ExecutorService to manage the threads that send e-mails, so that you don't get a potentially unlimited number of threads when many users are calling this functionality at the same time.
You should use the start() method to spawn as a new thread. If you call run() directly it is run in the same thread. See https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html
Use start() instead of run().
Run will execute it on the existing thread.
Start will execute it on a new thread.
So change your code to the following if you want it to execute asynchronous:
new SendMail(emailService).start();
new SendMail(emailService).start(); - will start a new Thread and will execute SendMail.run(); in the new Thread.
new SendMail(emailService).run(); - is just a method call which executed in the same thread.
I'm trying to make countdown before the page redirected to another page like "You'll be redirected in (x) seconds. x is one of the String values those saved in a ArrayList.
I tried this, but it waits end of the for loop to write down seconds and result is like "54321".
ArrayList<String> seconds=new ArrayList<String>();
seconds.add(5);
seconds.add(4);
seconds.add(3);
seconds.add(2);
seconds.add(1);
<font size="45"><% for (int i=0;i<5;i++)
{
out.write(seconds.get(i));
}
%></font>
You need to start a new thread and call this method from that thread, since otherwise it will be called only after the main thread is busy redirecting.
Thread example:
public class PrinterThread extends Thread {
private Thread thread;
private void method(){
--add code here
}
public void start() {
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
#Override
public void run() {
method();
}
}
and call it like this:
PrinterThread thread = new PrinterThread();
thread.start();
You can flush the response writer after every second, but this is a bad solution nowadays.
The best way to make a redirect after a countdown is to write a JavaScript to make it. Put the JavaScript in your html and let it control the countdown and the redirect.
I would like to have an application which either loads or saves data through a HTTP request, however the data must interact with the UI thread. Ideally, I would like a single thread to use an IF statement on a message to determine if the request is to "load" or "save".
What would be the simplest way of doing this with the smallest amount of code?
Also, do instances of Handlers run on individual threads?
EDIT: This is the code I am using now:
Handler doStuff = new Handler(){
#Override
public void handleMessage(Message msg){
if(msg.what == 1){
// Load all the information.
// Get the ID from sharedPrefs
SharedPreferences details= getSharedPreferences("details", 0);
String ID = patDetails.getString("id", "error");
// Load up the ID from HTTP
String patInfo = httpInc.getURLContent("info.php?no="+AES.encrypt("387gk3hjbo8sgslksjho87s", ID));
// Separate all the details
patientInfo = patInfo.split("~");
}
if(msg.what == 2){
// Save the data
}
}
};
Eclipse halts the debugging and displays, "Source not found" for StrictMode.class
I suppose it's because it's using the Main thread to access the internet although it's running in individual threads.
Any idea.
Handlers do run on individual threads. Check that link. You should also check out AsyncTask.
I would propose submitting the jobs as Runnable to a single-threaded ExecutorService:
public class SomeClass {
private ExecutorService execService = Executors.newSingleThreadExecutor();
public void doSomething() {
final String someUiData = // retrieve data from UI
execService.submit(new Runnable() {
#Override
public void run() {
// so something time-consuming, which will be executed asynchronously from the UI thread
// you can also access someUiData here...
}
});
}
}
This way, the UI thread will not block whereas you can easily submit a different Runnable for different operations and the ExecutorService will completely take care of keeping it async.
Edit: If you need to interact with the UI, do so before becoming asynchronous and keep the result in final variables.