Report back progress of long request on Google Appengine - java

I'm working on implementing a web application, with Google Appengine as the backend, on which the expected behaviour is as follows:
User selects a couple of parameters for a complex analysis
User presses 'Start'
An initially empty 'response' page is returned to the user, but the processing continues
The analysis somehow continues on the server and as partial results are being computed they are shown / added in the response page.
I'm expecting the total computation to be around 30-40 seconds (so way under the 60 seconds required by Appengine).
Steps 1 and 2 are trivial. I know step 4 could somehow be completed using step AJAX, but I'm not sure how exactly I could implement step 3.
Thanks!

You can use the task queue and the datastore. You need 3 handlers:
The task handler, doing the hard work. It will store its progress in the datastore.
A handler that starts the task in the background and returns the 'blank' page
A handler to get the status
Note: The page cannot be really blank. It must have javascript on it that checks the status. I think this is true with the Channel API too.
Anyway heres the code in Python:
class LongTaskStatus(ndb.Model):
is_complete = ndb.BooleanProperty()
percentage = ndb.FloatProperty()
messages = ndb.StringProperty(repeated=True)
class LongTaskHandler(webapp2.RequestHandler):
def get(self):
# Query for existing status model or create a new one
# Does work ...
# Update progress
status = LongTaskStatus()
status.messages.appen('Still busy...')
status.put()
# Does work ...
class StartHandler(webapp2.RequestHandler):
def get(self):
# start the task
taskqueue.add(url='/longtask')
# Return a page which uses javascript to check the progress every few seconds
template = JINJA_ENVIRONMENT.get_template('taskprogress.html')
self.response.write(template.render(template_values))
class CheckTaskStatus(wenapp2.RequestHandler):
def get(self):
query = LongTaskStatus.query().fetch(1)
result = {}
if query:
status = query[0]
result = {
'is_complete': status.is_complete,
'percentage': status.percentage,
'messages': status.messages
}
self.response.write(json.dumps(result))
and heres the "blank" page:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="status"></div>
<script>
window.setInterval(function(){
$.get( "ajax/test.html", function( data ) {
$( ".status" ).html( data );
});
}, 5000);
</script>
</body>
</html>
Edit: Other option without Task Queue
If you have a unique way of identifying the task before it is started you could potentially speed this method up by not using the task queue api.
Heres how:
Call the LongTaskHandler via javascript
Redirect to a loading page, which calls CheckTaskStatus.
This should be faster than using task queues, but unfortunately you need a way to identify the task before it is started. eg userid, session etc

Check out the Channel API. It allows you to push messages from server to client.

Related

Java servlet and Ajax uploading files progress bar

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.

How to generate request using JSP?

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.

How can i write a loading page with play framework

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.

Showing a progress bar when downloading a file from the server

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();

Auto Log Off once the session expires

Our application logs off after 30 min and gets redirected to login page,i am specifying session timeout in web.xml and using a requestProcessor for redirecting.I want to show to the user a message saying your session got expired once the session expires,how can i do that.Auto log off ?
I would like to prompt the error message on the page"The session is timeout, please login again" . Then how could I detect the session is timeout? will any methods trigger automatically?
Create an activity checker which checks every minute if any user activity has taken place (mouseclick, keypress) and performs a heartbeat to the server side to keep the session alive when the user is active and does nothing when the user is not active. When there is no activity for 30 minutes (or whatever default session timeout is been set on server side), then perform a redirect.
Here's a kickoff example with little help of jQuery to bind click and keypress events and fire ajax request.
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(document).ready(function() {
$.active = false;
$('body').bind('click keypress', function() { $.active = true; });
checkActivity(1800000, 60000, 0); // timeout = 30 minutes, interval = 1 minute.
});
function checkActivity(timeout, interval, elapsed) {
if ($.active) {
elapsed = 0;
$.active = false;
$.get('heartbeat');
}
if (elapsed < timeout) {
elapsed += interval;
setTimeout(function() {
checkActivity(timeout, interval, elapsed);
}, interval);
} else {
window.location = 'http://example.com/expired'; // Redirect to "session expired" page.
}
}
</script>
Create a Servlet which listens on /heartbeat and does basically just the following:
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
request.getSession();
}
to keep the session alive.
When you store the logged-in user in the session, it will be "automagically" logged out whenever the session expires. So you don't need to manually logout the user.
Create a Listener class implementing HttpSessionListener and define it in web.xml
This will notify you when any session is destroyed. Use the sessionDestroyed() method.
See a full example here:
http://www.mkyong.com/servlet/a-simple-httpsessionlistener-example-active-sessions-counter/
Either it may be simple servlet, spring-mvc or spring-security auto logout is not possible without perfect client side logic.
Considering application will have both type of request
AJAX and
form submission/page reload
Auto logout needs very calculated logic. Presenting my autologout functionality implementation with following
Advantages.
1. No extra call/request is used to achieve this. considering performance impact if more than 10k active users and extra calls to achieve auto logout.
2. One line configuration using tag.
3. Works flawlessly even if user opens multiple tab or multiple window.
4. It intimates you before 30 seconds of session invalidation, so if you have filled form and not submitted, you can keep session alive(extend session by one click). So user less likely to loose unsaved data.
Usage
1. Include auto logout script in required JSP pages as given below.
....
</body>
<jsp:include page="../template/autologout-script.jsp"></jsp:include>
</html>
2. Create a JSP page, autologout-script.jsp and add below code.
Note: No editing/configuring is required
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<script>
$(document).ready(function()
{
var timeOutTimeInSeconds = ${ timeOutTimeInSeconds };
var showTimerTimeInSeconds= ${ showTimerTimeInSeconds };
var sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);
var timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);
var badgeTimerId;
window.localStorage.setItem("AjaxRequestFired", new Date());
function redirectToLoginPage(){
//location.href = '<c:url value="/" />'+'${loginPageUrl}';
window.location.reload();
}
$(document).ajaxComplete(function () {
resetTimer();
});
$(window).bind('storage', function (e) {
if(e.originalEvent.key == "AjaxRequestFired"){
console.log("Request sent from another tab, hence resetting timer")
resetTimer();
}
});
function resetTimer()
{
showTimerTimeInSeconds= ${ showTimerTimeInSeconds };
console.log("timeOutTimeInSeconds : "+timeOutTimeInSeconds)
window.localStorage.setItem("AjaxRequestFired", new Date());
window.clearInterval(sessionCheckIntervalId);
sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);
window.clearInterval(timerDisplayIntervalId);
timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);
hideTimer();
}
function showTimer()
{
$('#sessionTimeRemaining').show();
$('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
window.clearInterval(timerDisplayIntervalId);
badgeTimerId = setInterval(function(){
$('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
}, 1000);
}
function hideTimer()
{
window.clearInterval(badgeTimerId);
$('#sessionTimeRemaining').hide();
}
});
</script>
3. Configure session attributes to configuring timeout setting
Note: Configure this after session creation. You can implement HttpSessionListener sessionCreated method and set the following configuration as per your requirement.
session.setMaxInactiveInterval(300);
session.setAttribute("timeOutTimeInSeconds", 300);
session.setAttribute("showTimerTimeInSeconds", 30);
4. Add below html for displaying timer.
Note: it can be moved to autologout-script template page if you are good at CSS. Hence you can avoid to add this in each and every page.
Include bootstrap or add your custom css.
<span class="badge badge-primary" title="click to keep session alive" id="sessionTimeRemaining"
onclick="ajaxSessionRefresh()" style="display:none;">
<i class="badge badge-danger" id="sessionTimeRemainingBadge" style="float:left">30</i>
<small>Refresh</small>
<i class="glyphicon glyphicon-refresh"></i>
</span>
That is all about a simple auto logout implementation.
You can download working example from my github repository
Autologout using simple servlet example
Autologout using spring-security java configuration example
Autologout using spring-security xml configuration example
Logic Explained
Case 1: On Page load
Here logic is simple, on page load set timer of interval equlas to maxInactiveInterval. after timeout redirect to login page.
Case 2: Keep track AJAX calls
Now considering AJAX requests, you can use .ajaxStart() or .ajaxComplete() callbacks of jquery so that if any ajax request is fired you can reset the interval.
Case 3: Tracking multi tab/window activity
Intertab communication is done to synchronize state of each tab. Used localStorage on change event.
Limitations/Improvements required
1. If maximum allowed session is one, if session is taken from another system, AJAX request will fail. It needs to be handled to redirect to login page.
2. Use ajaxStart() instead of ajaxComplete() to have exact sync of idleTime values between server and browser.
Requirements
1. Jquery
Alternatives to current implementation compared
1. Setting Refresh header in http response. (Not works for AJAX requests)
response.setHeader("Refresh", "60; URL=login.jsp");
Setting meta refresh tag in HTML (Not works for AJAX requests)
<meta http-equiv="refresh" content="60; url=login.jsp">
Configuring Activity checker
Keeps session alive by repeated AJAX request. Tracks idle time and makes logout request after timeout.
No doubt it is a good one with simple logic. But i want to just ink my observations.
Performance impact if 2 requests are made per minute to keep session alive and 50k active users. 100k requests per minute.
Intertab communication If two tabs are open, one tab is receiving activity but other tab is not receiving activity, that tab fires logout request and invalidate session even though activity is present in other tab. (But can be handled)
Force logout approach It is a client is dominated over server to invalidate session.
If you're using servlet sessions, you can check to see if the session the jsp / servlet is returning is new using the isNew() method. If yes, then the user's session has expired and you can display the relevant messages.
Include a javascript utility function inside your JSP and ping the server every 31 minutes.
The above mentioned utility function should be using setTimeout() JS function internally.
setTimeout ( "checkServerSession()", /* intervalInMilliSeconds */ 31000);
Note that
checkServerSession()
is a regular JS function which may fire HTTP requests. If the request is successful session exists otherwise show the prompt to the user.

Categories