I would like to implement a page that be displayed to the user whilst a system command is run. As soon as the command completes the user should be routed to another page.
What are some strategies to implement this?
(A solution without javascript would be ideal)
It can definitely be done. You want to look at Asynchronous programming with HTTP in the documentation, it explains how to do this in a non-blocking way. You will need a little bit of javascript for the redirecting part though.
And I don't know what you mean with "system command" but you probably want to create a job for it, so you can trigger it with a request. You can then poll it until it's finished and then redirect the user. But really the documentation does an infinitely better job at explaining it then I'm doing now.
Here's an example of a controller action where I assume your system command returns some kind of String output for the user. When the Job is completed it will sent a response to the user, thus triggering the success handler in the javascript example.
public static void executeSystemCommand(String input) {
Promise<String> outputPromise = new SystemCommandJob(input).now();
String output = await(outputPromise);
renderText(output);
}
Basically if you're using jQuery's $.ajax you can use the complete event to poll the data (just do the request again if it didn't succeed within the timeout time) and use the success/done event to redirect the user when the application responds to indicate that the "system command" is done running.
Example of a function you could use:
function poll(){
$.ajax({
url: "/systemcommand",
success: function(data){
// redirect to next page here
document.location.href = '/output'
},
complete: poll,
timeout: 20000
});
};
There is also a great example on long polling in javascript on StackOverflow.
Related
I'm working on a web app, which is communicating with the server with AJAX requests. A special type of "close" request takes 5 secs, which the web app should just fire-and-forget, the result is irrelevant. Due to browser behaviors (only limited number of simultaneous AJAX requests are performed), a 5-sec request may stuck other AJAX requests, which is unacceptable.
The smart folks here in StackOverflow has adviced me to write a small server-side proxy, which the web app should call instead of the original 5-sec one. The proxy should response immediatelly, close response channel, then perform a HTTP request and wait for it, spending the 5 secs server-side, instead of client-side. (The original question is here: See Is there a way to perform fire-and-forget AJAX request? )
The server is a Tomcat with JSP, and I can write small JPS pages. (I'm not an experienced JSP ninja, but I don't afraid of Java.) My question is: is it legal to write such a JSP, or what's the best practice:
send the response,
close reply channel (is out.close() enough?), in order to end the AJAX request at client-side,
fire and process (actually: just drop response) a HTTP request "in background", which may take as long as 5 secs?
It's not (only) your browser you should worry about. Blocking a tomcat thread for 5s severly limits your max-users as well (how many requests per second do you need to handle ultimately?)
So making it "more" asynchronous in the server might make sense.
Doing it in JSP (with Sriplets?!) alone will noway be a robust implementation - but if you need to do it that way, you should think about starting the "work to do" in a separate Thread.
So instead of
<%
do_something_heavy();
%>
You'll do like
<%
new Thread(new Runnable() {
public void run() {
do_something_heavy();
}
}).start();
%>
There's other options as well (JMS, ExecutorService, Spring #Async...) but this should get you started quick.
First the best is to separate business logic from view: it means write java code on a servlet and delegate only the view aspect to the jsp.
To execute your task asynchronously in the servlet code you can:
Invoke a submit method of an ExecutorService
Make a call to a JMS
Manually create a thread and start it
Then you can forward to the jsp.
TIP: It is possible to assign an id to the long task and return it in the jsp with a link to monitor the status of the task.
Basically you do something like that:
Accept the request
Start asynchronously a thread to execute the long task
Return immediately without waiting for the long task termination
Or using an id:
Accept the request
Calculate the id of the task
Start asynchronously a thread to execute the long task with the desired id
Return immediately a link with the id of the long task without waiting for the termination
Recently I am working on a project which using ajax to call java servlet, and the request takes more than 10 sec to get the response, so I need to make sure during this time user won't be able to submit the form again, and my current approach is detect submit button click event and disable submit button, once success or error function is triggered in ajax, enable the button again, but this is not a good approach.
---- Edit ----
Sorry, I wasn't explain clearly, what I mean is this can stop non technical background user, but if any one intend to attack the site or whatever reason,
they can simply modify html code to enable the button, and do another submit,
Before I tried another way which set a cookie interval after form submit, and check the cookie when request finish, but just wondering whether there is any other way to do this, this question is purely for learning purpose.
Sorry for my English:)
I dont see anything wrong with disabling the button, that is what I frequently use, because this not only provides an indication that the system acknowledged your click but also prevent the user from clicking again.
If for some reason you dont like that you can disable the underlying method call something like this:
var isSubmitting = false;
function handleClick(){
if (!isSubmitting)
{
isSubmitting = true;
$.ajax(
"http://yourservice/submit" {
data: {someData:123},
contentType: 'application/json',
type: 'POST',
success: function(){
isSubmitting = false;
},
});
}
}
About your edit, the cookie sounds like a good approach, basically you need something that the server is going to pass to the client, and then check on submit. once that has been authorized the server will prevent processing of further requests with the same parameter.
But bear in mind that a malicious user would spawn thousands of requests to get cookies and then perform all the submissions anyway, so it is not really a defence against attackers, for that you would have to implement some form of throttling.
So in the end if you just want to prevent accidental submissions the button hide will suffice.
Something I have done and has been successful is a combination of what you described and preventing the function called by the button to execute twice. I do this by keeping a variable that gets set to true with the first request, then on subsequent request I check for it, if it's true, I don't do anything. Something like this:
var isRequestAlive = false;
var submit = function(){
if(!isRequestAlive){
isRequestAlive = true;
doAjaxStuff("", function(){
isRequestAlive = false;
})
}
}
Currently, i using XmlHttpRequest to uploading files to the server using HTML5 capabilities.
There's progress bar:
xhr.upload.addEventListener('progress', function(e) {
var done = e.position || e.loaded, total = e.totalSize || e.total;
console.log(done);
});
... everything works fine, but it doesn't consider processing the file by server. So it shows 100% uploaded even when file weren't created yet.
The file receiver is Java servlet, which able to response only after return. So here's no way to count the percents left by its response.
Whether there are ways around it?
If the processing the server does takes a long time and you want to give feedback while it happens, here's a rough outline of a solution. This will take a while to implement so it's only really worth doing if the processing is slow.
Modify your servlet to do its work asynchronously, and return a 201 Accepted response to the client.
As the server processes the file, set the progress on an object, typically a ConcurrentHashMap injected with Spring if that's what you're using.
Expose an API that queries the current progress of the task without blocking for it to complete.
In your javascript, poll this API until the task completes, and show the progress.
You can return a tracking ID in the response at step 1 if you need to.
well I'm doing a web application, which processes a file after it's done uploading and I want somehow the user to to be able to get some info about the progress. Now I was thinking of creating a jsp progress page, which would sleep for 5 seconds, then generate a request and supply it with the filename that we want to know the progress of. So how do I do this, or is there a better way? Maybe JavaScript can do the desired actions? So what would you guys suggest? Thanks.
You need to poll the server using ajax. If you're using Java, the Apache FileUpload library has an interface called ProgressListener, which you implement to determine the upload's progress. You can track percentage received or just mark a file complete when it's complete. On the client side, you check the progress every few seconds until you see it's finished. Also, if you want your user to appear to remain on the same page, try setting the target attribute of your form to the id of a 0x0 iframe on submit.
If you decide to code the javascript, a simple polling function might look like this:
function poll(uploadId) {
$.ajax({
url: '/path/to/upload/status/servlet',
type: 'POST',
data: 'id=' uploadId,
dataType: 'json',
timeout: 10000,
error: function(err){
// handle error
},
success: function(data) {
var status = data["status"];
if (status == 'finished')
{
// completed upload logic
}
else
{
setTimeout(function() {
poll(uploadId);
}, pollingIntervalInMillis);
}
}
});
}
Poll the server (requires a progress API) periodically (via javascript) for the processing status and update the page accordingly when you get a positive result.
I need to show a progress bar to the user who requests a file to download. I am using J2EE application to generate the file. User will submit the form data to get the file. The server takes all the submitted data manipulates, generates and sends a PDF file back to Client.
So I want to show a progress bar to the user till the file comes to the Client side.
Is there any way to do this ?
If I understand you well, you want to show a progress bar until your server is ready to send a file, not to show the progress of the file beeing downloaded.
If that is true, you're dealing with a tough excercise. A reliable progressbar needs to know (pretty exact) what you're doing and how long it will take. In your case, there are lots of unreliable factors (one of them, maybe the biggest, is the web itself).
So most developers use some kind of an "endless" animation to display "work in progress".
update
Based on your comment, the easiest way to display a "work in progress" animation would look like
$.ajax({
url: "/myscripts/myserverscript",
type: "POST",
data: {
foo: "bar"
},
dataType: "text",
beforeSend: function(xhr){
// display a progress animation
},
complete: function(xhr, status){
// hide the animation
}
...
});
In the case of a single request. You may also setup a global ajax event handler for both (.ajaxStart() and .ajaxStop()) to setup the show/hide functionallity.
References: .ajax(), .ajaxStart(), .ajaxStop()
progress bar for server side file generation:
We assume that the server needs many seconds to generate the file. This event is triggered by the original request, a blocking operation. When this finishes the file will have been generated and it'll be dispatched back to the client.
At the same time you want, via other requests (ajax), to be calling the server and getting a percentage back for the file which is currently being generated for the particular user.
The glue parts here are:
when the original request is generating the file it needs to store the progress in frequent intervals (i.e every 10%). Storing this data in the http session will work OK.
the other requests (ajax) simply need to be able to pull this information out of the http session
synchronizing (serializing access) on the http session, something that some web apps commonly do, is out of the question, since the other requests (ajax) would simply block until the original request finished
on the client side it's all html+javascript to provide the interaction you need (animated progress bar). Even if the intervals are very rough (jumping from 10% to 20% to 30%) you can animate the bar with jQuery. I've done it once in the past and it looks great.
progress bar for file download:
it's best to leave this to the browser's native dialog.
In Java you just wrap a javax.swing.ProgressMonitorInputStream around the input stream, but be aware that unless the server is sending in chunked streaming mode the display won't really mean anything, as the entire response will have been read into memory before the first byte is delivered to Java.
Using XMLHttpRequest you can download file and show progress.
showProgressBar();
var xhr = new XMLHttpRequest();
xhr.open("GET", 'https://upload.wikimedia.org/wikipedia/commons/d/dd/Big_%26_Small_Pumkins.JPG', true);
xhr.responseType = "blob";
xhr.onprogress = function (e) {
console.log(e.loaded / e.total * 100);//shows downloaded percentage
setProgressBarPercentage(e.loaded / e.total * 100);
}
xhr.onload = function () {
hideProgressBar();
var urlCreator = window.URL || window.webkitURL;
var url = urlCreator.createObjectURL(this.response);
var link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', 'FILENAME');
link.click();
}
xhr.send();