最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Reload jsp page after computations - Stack Overflow

programmeradmin2浏览0评论

So my project demonstrates the results of a k-means clustering algorithm using servlets. When I cluster for the first time, everything works well and my program relocates the user to the results.jsp page.

But because k-means is a bit random at selecting centroids, I added a re-cluster feature in my code in the results.jsp which re-clusters my data with the settings originally used.

My servlet code for re-clustering looks like this:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    do stuff...
    //reload results page
    String htmlResponse = "<html><header><meta http-equiv=\"refresh\" content=\"0; URL='results.jsp\" /></header></html>";
    writer.println(htmlResponse);
    //but this doesnt work...
}

So after the clustering is done, I want to reload the resutls.jsp page. I tried adding a JavaScript to my jsp page since refresh is a client side operation. But the problem with that approach is that refresh happens before the clustering is fully pleted. So I get Exceptions when trying to print the results. Any advice?

My refresh script looks like this:

<script type="text/javascript">
        function refresh() {
            location.reload(true);
        }
</script>

<script type="text/javascript">
            function reCluster(cluster_id) {
                recluster();
            }
        </script>

and on the .jsp i'm calling this like this:

<form class="form-horizontal" action="generateArff" method="GET" enctype="multipart/form-data">
     <button type="button" class="btn btn-default btn-lg" onclick="reCluster(); refresh();">
         <span class="glyphicon glyphicon-refresh"></span> Re-Cluster
     </button>

     <button type="submit" class="btn btn-info btn-lg">
         <span class="glyphicon glyphicon-download"></span> Generate file
     </button>
</form>

Can you advice on the matter?

So my project demonstrates the results of a k-means clustering algorithm using servlets. When I cluster for the first time, everything works well and my program relocates the user to the results.jsp page.

But because k-means is a bit random at selecting centroids, I added a re-cluster feature in my code in the results.jsp which re-clusters my data with the settings originally used.

My servlet code for re-clustering looks like this:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    do stuff...
    //reload results page
    String htmlResponse = "<html><header><meta http-equiv=\"refresh\" content=\"0; URL='results.jsp\" /></header></html>";
    writer.println(htmlResponse);
    //but this doesnt work...
}

So after the clustering is done, I want to reload the resutls.jsp page. I tried adding a JavaScript to my jsp page since refresh is a client side operation. But the problem with that approach is that refresh happens before the clustering is fully pleted. So I get Exceptions when trying to print the results. Any advice?

My refresh script looks like this:

<script type="text/javascript">
        function refresh() {
            location.reload(true);
        }
</script>

<script type="text/javascript">
            function reCluster(cluster_id) {
                recluster();
            }
        </script>

and on the .jsp i'm calling this like this:

<form class="form-horizontal" action="generateArff" method="GET" enctype="multipart/form-data">
     <button type="button" class="btn btn-default btn-lg" onclick="reCluster(); refresh();">
         <span class="glyphicon glyphicon-refresh"></span> Re-Cluster
     </button>

     <button type="submit" class="btn btn-info btn-lg">
         <span class="glyphicon glyphicon-download"></span> Generate file
     </button>
</form>

Can you advice on the matter?
Share Improve this question edited Feb 24, 2017 at 18:21 Jack asked Feb 23, 2017 at 22:35 JackJack 7223 gold badges9 silver badges26 bronze badges 10
  • Sorry, to me it is not clear where the exceptions are ing from - are they thrown by code inside the jsp you are trying to refresh? Do they get thrown the first time too or only after refresh? – Marco Bolis Commented Feb 23, 2017 at 22:38
  • You could do call the service from the client using AJAX or similar, and when the job is done, receive the callback. – José Quinto Zamora Commented Feb 23, 2017 at 22:41
  • @MarcoBolis for sanity checks, I setted up the clusters so initially they have an index of -1 so if something was going on during my implementation I would pick up the error. For the re-cluster process, I 'm reseting all the existing clusters to their original settings. aka index -1 so when I print them because they are stored in an ArrayList, well you can imagine what will happen with index of -1 – Jack Commented Feb 24, 2017 at 11:45
  • @JoseQuintoZamora I'm already doing that. I'm calling the service from AJAX. Thing is that after the service is called, the client goes to the refresh service very fast, before there is enough time to calculate the new clusters – Jack Commented Feb 24, 2017 at 11:47
  • @Jack if you are calling the service through AJAX, then why are you refreshing? Sorry, I'm trying to better understand the issue – Marco Bolis Commented Feb 24, 2017 at 12:05
 |  Show 5 more ments

2 Answers 2

Reset to default 4

So, @MarcoBolis provided a very good and informative answer, and definitely gets a +1 from me.

However, I think that the most simple solutions are the most effective ones.

As I see your code, I can only assume that you are using an AJAX request to your servlet because I see 1 html form with 2 buttons that do different things. So, what I would do, is go to your ReClusterServlet and add the following:

public class ReClusterServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        //Call your Cluster class and methods...
        //wait for it...

        //return something to your AJAX call.
        writer.print("refresh page");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}

now, go to your AJAX request. I assume it will be in the recluster() function I see you are calling in the head of your http code. so it must be something like this:

function recluster() {
    $.ajax({
        url:'ReClusterServlet',
        type:'POST'
    });
}

This thing, is calling the servlet but did you know that when your servlet was done with the putation it returned a success message to it??? But you are ignoring it!!!

it's this line writer.print("refresh page");

Now re-write the function as follows:

function recluster() {
    $.ajax({
        url:'ReClusterServlet',
        type:'POST',
        success:function() {   //handle the successful return by reloading the current page
            location.reload();
        }
    });
}

Again, these are speculations because they seem a bit inplete, so feel free to correct me if I'm wrong, but I have a strong feeling that you need to study a bit more on the client-server architecture, JQuery and AJAX

I understand you are issuing an AJAX call to the server to start an asynchronous task, performing the clustering.

You could notify the client of task pletion by means of a message returned through the XHR:

function reCluster() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'ReClusterServlet', true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            // Refresh if everything went OK
            if (xhr.responseText === 'Done') refresh();
        }
    };
}

The tricky part is handling the notification Java-side.

If you are using Servlet API 3.x+, then you are lucky: just make your request an async one:

public class ReClusterServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Defer the request to this HTTP request to later
        AsyncContext asyncCtx = request.startAsync(request, response);
        asyncCtx.setTimeout(0);

        // Start the long running putation in another thread,
        // passing the AsyncContext
        new MyLongRunningTask(asyncCtx).start();
    }
}

Then plete it in the other thread:

public class MyLongRunningTask {
    private final AsyncContext asyncCtx;
    public MyLongRunningTask(AsyncContext asyncCtx) {
        this.asyncCtx = asyncCtx;
    }

    // ...

    private void finish() {
        // Send the response asynchronously
        Writer out = this.asyncCtx.getResponse().getWriter();
        out.println("Done");
        out.flush();

        this.asyncContext.plete();
    }
}

If instead you are using an older version of the Servlet API, you will have to make the serving thread (the one on which the doGet method gets called) block until the putation is finished.

If you have a reference to the thread on which the putation is performed available, you can call its join method to await termination:

public class ReClusterServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Thread async = new MyLongRunningThread();
        async.start();

        // Await end of putation
        try {
            async.join();
        } catch (InterruptedException e) {
            // Computation was interrupted...
        }

        response.getWriter().println("Done");
    }
}

If instead the putation task is queued for execution, for example by an ExecutorService, you have to use wait/notify:

public class ReClusterServlet extends HttpServlet {
    private ExecutorService executor;
    // ...

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        MyLongRunningRunnable task = new MyLongRunningRunnable();

        synchronized (task) {
            executor.execute(task);

            // Put current thread to sleep until task is no more RUNNING
            // (assuming state is a property of MyLongRunningRunnable)
            while (task.getState() == State.RUNNING) {
                try {
                    task.wait();
                } catch (InterruptedException e) {
                    // Computation was interrupted...
                }
            }
        }

        response.getWriter().println("Done");
    }
}

Assuming that the async task calls notify when done:

public class MyLongRunningRunnable implements Runnable {
    private State state = State.RUNNING;

    // ...

    private synchronized void finish() {
        this.state = State.DONE;
        this.notify();
    }
}

NOTE that these two solutions that block the request thread may cause performance issues if you have lots of ining requests and the clustering job takes very long.

发布评论

评论列表(0)

  1. 暂无评论