0

I want to implement a timeout for a process, if it takes more than X amount of seconds, I want it to stop and execute the return statement regardless. In my real world use I would be calling a REST API, apiCallController() represents the Controller.

With what I've tried below everything just keeps on executing regardless.

How do I pull this off?

EDIT: If what I'm trying to achieve works, the long running task won't complete which means that the line

System.out.println("End http/SSH stuff...");

will never print, and this line

response = "Call successful...";

will not be executed either, leaving the response variable as originally initialized

String response = "Call aborted...";

But I still need to return the response after the timeout

I've been testing in this Java fiddle (you can just paste the code): https://javafiddle.leaningtech.com/

Thanks.

import java.util.*;
import java.lang.*;

  public class JavaFiddle
  {
    public static void main(String[] args)
    {
        String response = apiCallController();
        System.out.println(response);
    }
    
    public static String apiCallController() {
        System.out.println("creepy...\n");
        int timeoutSeconds = 2;
        int longRunningTaskDurationSeconds = 5;
        String response = "Call aborted...";

        try 
        {
            new Timer().schedule( 
                new TimerTask() {
                    @Override
                    public void run() {
                        System.out.println("Timeout reached, aborting... (This is where I want everything to stop without killing JVM/Tomcat)");
                        // System.exit(0); This guy shut tomcat down x_X
                        return;
                    }
                }, 
                timeoutSeconds * 1000
            );
            
            System.out.println("Start http/SSH stuff...");
            Thread.sleep(longRunningTaskDurationSeconds * 1000);
            System.out.println("End http/SSH stuff...");
            
            response = "Call successful...";

        } 
        catch(InterruptedException e)
        {
            System.out.println(e);
        }

        System.out.println("\npasta...");
        
        return "\n" + response;
    }
    
  }

EDIT 2: From the accepted answer, I just refactored a touch:

import java.util.*;
import java.lang.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;

  public class JavaFiddle
  {
    public static void main(String[] args)
    {
        System.out.println("creepy...\n\n");

        String response = apiCallController();
        System.out.println(response);

        System.out.println("\n\npasta...");
        
    }
    
    public static String apiCallController() {
        String response = "Stuff TIMED out...";
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        Callable<String> r = () -> {
            try {
                System.out.println("Start http/SSH stuff...");
                TimeUnit.SECONDS.sleep(5);
                System.out.println("End http/SSH stuff...");
                return "Stuff COMPLETED successfully...";
            } catch (InterruptedException e) {
                throw e;
            }
        };
        Future<String> task = executor.submit(r);

        try {
            response = task.get(3, TimeUnit.SECONDS);
        } catch(InterruptedException | TimeoutException e) {
            task.cancel(true);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        // Need to shutdown executor (think of it is master thread here)
        // You may want to control this behaviour outside of this function call
        executor.shutdown();

        return "\n" + response;
    }
    
  }
4
  • What is timeout*1000 ? Commented Apr 8, 2021 at 1:26
  • I'm converting seconds into milliseconds. Commented Apr 8, 2021 at 1:28
  • Have you seen Asynchronous timeouts with CompletableFutures in Java 8 and Java 9? Commented Apr 8, 2021 at 1:31
  • By the way, you can replace timeoutSeconds * 1000 with the more obvious Duration.ofSeconds( timeoutSeconds ).toMillis(). Even better, change the type of timeoutSeconds to be a Duration object named timeout. Commented Apr 8, 2021 at 1:55

1 Answer 1

1

Your task is finished after printing to console but your timer is waiting for more task, hence still running and because it is still running the main function won't exit.

If you don't have further task you need to cancel your timer.

try
        {
            Timer timer = new Timer();
            timer.schedule(
                    new TimerTask() {
                        @Override
                        public void run() {
                            System.out.println("Timeout reached, aborting... (This is where I want everything to stop without killing JVM/Tomcat)");
                        }
                    },
                    timeoutSeconds * 1000
            );

            System.out.println("Start http/SSH stuff...");
            Thread.sleep(longRunningTaskDurationSeconds * 1000);
            System.out.println("End http/SSH stuff...");

            response = "Call successful...";

            timer.cancel();

        }
        catch(InterruptedException e)
        {
            System.out.println(e);
        }

        System.out.println("\npasta...");

        return "\n" + response;

Edit

Since the question is updated with real use case, I am adding a edit here to suggest an answer for that use case (Earlier part may not relevant to the question now).

This is my solution to your problem, have a look. I have used Future and ScheduledExecutorService to achieve it.

public static String apiCallController() {
        System.out.println("creepy...\n");
        
        String response = "Call aborted...";

        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

        Callable<String> r = () -> {
            try {
                // To mimick the actual call
                TimeUnit.SECONDS.sleep(5);
                System.out.println("call successful...");
                return "Some response";
            } catch (InterruptedException e) {
                System.out.println("Timeout reached, aborting... (This is where I want everything to stop without killing JVM/Tomcat)");
                throw e;
            }
        };

        Future<String> task = executor.submit(r);

        try
        {
            System.out.println("Start http/SSH stuff...");
            
            //Let's just wait for 3 secs for response to arrive
            response = task.get(3, TimeUnit.SECONDS);

            System.out.println("End http/SSH stuff...");

            response = "Call successful...";

        }
        catch(InterruptedException | TimeoutException e)
        {
            // cancelling a task, either it was interrupted (sleep call can be interrupted) or its timeout
            task.cancel(true);
        }catch (ExecutionException e) {
            //Something went wrong horribly
            e.printStackTrace();
        }

        System.out.println("\npasta...");

        // Need to shutdown executor (think of it is master thread here)
        // You may want to control this behaviour outside of this function call
        executor.shutdown();

        return "\n" + response;
    }
Sign up to request clarification or add additional context in comments.

2 Comments

I'm trying to prevent these lines from not executing: System.out.println("End http/SSH stuff..."); response = "Call successful...";. They still do with your solution.
Please update your question with this information, thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.