0

I'm writing a Spring web application and I'm mapping the "/do" URL path to the following Controller's method

@Controller
public class MyController
{
    @RequestMapping(value="/do",  method=RequestMethod.GET)
    public String do()
    {
        File f = new File("otherMethodEnded.tmp");
        while (!f.exists())
        {
            try {

                Thread.sleep(5000);

            } catch (InterruptedException e) {

            }
        }

        // ok, let's continue
    }
}

The otherMethodEnded.tmp file is written by one another Controller's method, so when the client calls the second URL I expect the first method to exit the while loop.

Everything works, except when the client calls the "/do" URL and then closes the connection before the response was received. The problem is that the server remains in the while (!f.exists()) loop even though the client is down and cannot call the second URL to unlock the while loop.

I would try to retrieve the connection status of the "/do" URL and exit the loop when the connection is closed by the client, but I cannot find any way to do so.

I tried with the HttpServletRequest.getSession(false) method but the returned HttpSession object is always not null, so the HttpServletRequest object is not updated in case of connection close of the client.

How can I check whether the client is still waiting for the risponse or not?

gvdm
  • 3,006
  • 5
  • 35
  • 73

1 Answers1

1

The simplest way to verify something is not right is to define a timeout value and then during your loop test if your time spent waiting has exceeded the timeout.

something like:

@Controller
public class MyController
{
    private static final long MAX_LOOP_TIME = 1000 * 60 * 5; // 5 minutes? choose a value

    @RequestMapping(value="/do",  method=RequestMethod.GET)
    public String do()
    {
        File f = new File("otherMethodEnded.tmp");
        long startedAt = System.currentTimeMillis()
        boolean forcedExit = false;
        while (!forcedExit && !f.exists())
        {
            try {
                Thread.sleep(5000);
                if (System.currentTimeMillis() - startedAt > MAX_LOOP_TIME) {
                    forcedExit = true;
                }
            } catch (InterruptedException e) {
                forcedExit = true;
            }
        }

        // ok, let's continue

        // if forcedExit , handle error scenario?
    }
}

Additionally: InterruptedException is not something to blindly catch and ignore. see this discussion

In your case I would really exit the while loop if you're interrupted.

You only know if the client is no longer waiting on your connection when you notice the output stream you write to (response.outputstream) is closed. But there isn't a way to detect it. (see this question for details)

Seeing as you've indicated your client does occasional callbacks, you could on the clientside poll if the other call has been completed. If this other call has completed, do the operation, otherwise return directly and have the client do the call again. (assuming you are sending json, but adapt as you require)

something like

public class MyController
{
    @RequestMapping(value="/do",  method=RequestMethod.GET)
    public String do()
    {
        File f = new File("otherMethodEnded.tmp");
        if (f.exists()) {
            // do what you set out to do
            // ok, let's continue


            // and return with a response that indicates the call did what it did
            // view that returns json { "result" : "success" } 
            return "viewThatSIgnalsToClientThatOperationSucceeded";
        } else {
            // view that returns json:  { "result" : "retry" }
            return "viewThatSignalsToClientToRetryIn5Seconds"; 
        }
    }
}

Then the clientside would run something like: (pseudojavascript as it's been a while)

val callinterval = setInterval(function() { checkServer() }, 5000);

function checkServer() {
    $.ajax({
        // ...
        success: successFunction
    });
}

function successFunction(response) {
    // connection succeeded
    var json = $.parseJSON(response);
    if (json.result === "retry") {
        // interval will call this again
    } else {
        clearInterval(callinterval);
        if (json.result === "success") { 
            // do the good stuff
        } else if (json.result === "failure") {
            // report that the server reported an error
        }
    }
}

Ofcourse this is just semi-serious code but it's roughly how i'd try it if I were to have the dependency. If this is regarding afile upload, keep in mind that this file may not contain all of the bytes yet. file exists != file = completely uploaded, unless you use move it. cp / scp / etc. is not atomic.

Community
  • 1
  • 1
Joeblade
  • 1,735
  • 14
  • 22
  • Thank you for your advices. The only problem in your answer is that I don't know a priori when the client will call the second URL so I cannot define a right timeout for the loop because I would compromise the operation. +1 for the good point about the InterruptedException – gvdm Nov 28 '14 at 13:33
  • The solution I'm using now is to let the client to periodically poll the server. This way the server knows if the client is online and should continue looping or not – gvdm Nov 28 '14 at 15:11
  • Probably you don't want to tie up a client indefinitely with a while loop. if the client already does a call back then I'll update my answer with an alternative – Joeblade Nov 28 '14 at 15:21