Setting up AJAX progress bar - java

Ok im new to AJAX. Kind of know about the lifecycle of a request, IE uses ActiveXObject stuff like that.
However now im faced with a real world problem. I have a servlet running in the background that performs a number of tasks and i would like to display a progress bar to show where the process is up to and to also show that something is actually happening.
So far in the server side i can calculate the number of processes that will take place before they begin, and easily enough add a counter to increment after each process completes - therefore enabling me to find the percentage i wish to show.
In terms of where i should output the data gathered from incrementing etc i'm not sure so far it is dormant as 2 integers in the processing java class.
Any help would be much appreciated.
So far i've taken a look at this and i guess thats kind of what im aiming for.
Cheers.

Basically, you'd like to store a reference to the task and inherently also its progress in the HTTP session (or in the Servlet context if it concerns an application wide task). This way you must be able to retrieve information about it on every HTTP request.
In JavaScript, you can use setTimeout() or setInterval() to execute a function after a certain timeout or at certain intervals. You can use this to repeatedly fire Ajax requests to request current status of the progress. Once the status is retrieved, e.g. in flavor of an integer with a max value of 100 (the percentage), just update some div representing a progressbar and/or the percentage text accordingly in the HTML DOM tree.
jQuery is very helpful in firing ajaxical requests and traversing/manipulating the HTML DOM tree like that. It minimizes the code verbosity and crossbrowser compatibility headaches.
Imagine that the doGet() of the servlet look like this:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String processId = "longProcess_" + request.getParameter("processId");
LongProcess longProcess = (LongProcess) request.getSession().getAttribute(processId);
int progress = longProcess.getProgress();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(String.valueOf(progress));
}
Then you can use it like follows:
function checkProgress() {
$.getJSON('progressServlet?processId=someid', function(progress) {
$('#progress').text(progress + "%");
$('#progress .bar').width(progress);
if (parseInt(progress) < 100) {
setTimeout(checkProgress, 1000); // Checks again after one second.
}
});
}

Related

Tomcat: set maximum connection for particular servlet [duplicate]

I got this servlet which return a pdf file to the client web browser.
We do not want to risk any chance that when the number of request is too much, the server is paralyzed.
We would like to make an application level (program) way to set a limit in the number of concurrent request, and return a error message to the browser when the limit is reached. We need to do it in applicantion level because we have different servlet container in development level(tomcat) and production level(websphere).
I must emphasize that I want to control the maximum number of request instead of session. A user can send multiple request over the server with the same session.
Any idea?
I've thought about using a static counter to keep track of the number of request, but it would raise a problem of race condition.
I'd suggest writing a simple servlet Filter. Configure it in your web.xml to apply to the path that you want to limit the number of concurrent requests. The code would look something like this:
public class LimitFilter implements Filter {
private int limit = 5;
private int count;
private Object lock = new Object();
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
boolean ok;
synchronized (lock) {
ok = count++ < limit;
}
if (ok) {
// let the request through and process as usual
chain.doFilter(request, response);
} else {
// handle limit case, e.g. return status code 429 (Too Many Requests)
// see https://www.rfc-editor.org/rfc/rfc6585#page-3
}
} finally {
synchronized (lock) {
count--;
}
}
}
}
Or alternatively you could just put this logic into your HttpServlet. It's just a bit cleaner and more reusable as a Filter. You might want to make the limit configurable through the web.xml rather than hard coding it.
Ref.:
Check definition of HTTP status code 429.
You can use RateLimiter. See this article for explanation.
You might want to have a look on Semaphore.
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
Or even better try to figure it out with the server settings. That would of course be server-dependant.
I've thought about using a static counter to keep track of the number of request, but it would raise a problem of race condition.
If you use a AtomicInteger for the counter, you will not have the problem of race conditions.
An other way would be using the Java Executor Framework (comes with Java 1.5). There you are able to limit the number of running threads, and block new once until there is a new free thread.
But I think the counter would work and be the easyest solution.
Attention: put the counter relese in a finally block!
//psydo code
final AtomicInteger counter;
...
while(true) {
int v = counter.getValue()
if (v > max) return FAILURE;
if(counter.compareAndSet(v, v+1)) break;
}
try{
doStuff();
} finally{
counter.decrementAndGet();
}
If you are serving static files, it's unlikely that the server will crash. The bottleneck would be the network throughput, and it degrades gracefully - when more requests come in, each still get served, just a little bit slower.
If you set a hard limit on total requests, remember to set a limit on requests per IP. Otherwise, it's easy for one bad guy to issue N requests, deliberately read the responses very slowly, and totally clog your service. This works even if he's on a dialup and your server network has a vast throughput.

Start performing an action every minute after getting to a web page

I am developing a Java web application using Spring.
What I would like to do is that after the user gets to a page, the code starts running a function every 10 seconds, keeping track on the time the last action was performed.
I tried to do so with a Scheduler but it starts running immediately - and not only after the user gets to a page.
#Scheduled(fixedRate = 60000)
public void run(String param) {
//just an example of action to be performed repeatedly
System.out.println("Previously performed action was " + new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(previousActionTime)) + " with " + param);
//update previousActionTime
previousActionTime.setSeconds(previousActionTime.getSeconds() + 10);
}
Moreover I don't know what it is a convenient way to store the time when the last action automated action was performed.
The scheduler should be somehow activated when browsing to the page:
#RequestMapping(value = "/hellopage", method = { RequestMethod.POST, RequestMethod.GET })
public String hellopage(HttpServletRequest request, HttpServletResponse response) {
// Activate scheduler
run(request.getParameter("param1"))
...
}
The scheduler (or whatever performs the automated actions) should stop as soon as the user gets again to the same web page triggering the automated actions and should run in background not blocking any other code from execution (to be precise, I cannot simply put a while loop with Sys.sleep in the function mapped to the page URL request why the page should do other things)
Any help?
Consider using ScheduledExecutorService.scheduleAtFixedRate for this as the Spring scheduler are independent of any users' request (which you have already observed and noted in question).
You may use shutdownNow to terminate the scheduler once the users' session is no longer valid / a new request is received. To achieve this you could maintain cache of previous executor(s) with user id (or any relevant information) to identify the instance which should be invalidated.
As an alternative you could use Timer and TimerTask if more fine grained control is required (however not recommended as noted here)
There are two common ways of achieving this.
The first is to run your timer client-side, in JavaScript, and then runs an AJAX/websocket/whatever call. This has many advantages - once the user navigates away from your site, the timer will stop, and you're not tying up server-side resources so your application will scale much more cleanly. This is by far the cleanest solution if your timer is linked to a single user.
The second is to use a message queue; pop a message on the queue and have an asynchronous process checking those messages, ideally aggregating multiple client sessions in a single database request. You need to figure out how to detect sessions timing out and remove the message from the message queue.
This approach is best when your timer is looking at information that's not tightly connected to the current user.

Best approach for checking time remaining in Spring Boot Session from browser / front end?

I have a Spring Boot app using Spring Session and Spring Security. Java # config. That's all working. I can set my timeout dynamically as I need.
What I need to do is check the remaining session time from the browser. I guess I could check the server with an ajax request every X minutes and have a servlet do a session.getLastAccessedTime() and a session.getMaxInactiveInterval() and do the math to see what time is remaining and return that. (not sure if that would refresh the lastAccessedTime though..)
But as with everything Spring-related, my first instinct is usually wrong. Ha. Plus if the browser is sitting idle for 30 minutes, seems silly to ping the server every ~minute when nothing has changed.
I'm seeing if anyone has a better solution for this. I'd think that maybe I could set a cookie with a epoch time (~1602002425) when the session will timeout. Then I can watch that value with a javascript function. But I don't see a way to do that in Spring. I tried making a CookieSerializer bean creating a custom cookie name, but there is not a way I can see to SET that value. (looks like a random base64 string value gets created for me) This is probably Spring preventing me from doing something the "wrong way". Then this cookie would need to be updated whenever the Session is refreshed as well.
I've spend a couple days looking from an answer online and all I see are ways to set that Session timeout value servers-side (which I can already so) or JavaScript solutions front-end that are not Spring specific.
End goal is to get a ~"session is about to expire.. refresh?" button to popup when client is getting close to timeout. Does anyone have an approach they've use that works for this? I feel like someone has to have solved this, but I am not seeing any posts that address this. Thanks!
=======
EDIT
I figured out I can use this code to set a cookie and return the lastAccessed time in the response body..
#RequestMapping("/api/timeout")
#ResponseBody
public String add(HttpServletRequest request, HttpServletResponse response) throws Exception
{
return getLastAccessed(request, response);
}
String getLastAccessed(HttpServletRequest request, HttpServletResponse response)
{
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession(false);
long lastAccessed = session.getLastAccessedTime();
final String cookieName = "timeout";
final String cookieValue = String.valueOf(lastAccessed);
final Boolean useSecureCookie = false;
final int expiryTime = 60 * 60 * 24; // 24h in seconds
final String cookiePath = "/";
Cookie cookie = new Cookie(cookieName, cookieValue);
cookie.setSecure(useSecureCookie);
cookie.setMaxAge(expiryTime);
cookie.setPath(cookiePath);
response.addCookie(cookie);
return String.valueOf(lastAccessed);
}
But as I feared, the lastAccessed timestamp gets updated every time I check for it. I need to find a way to check that from the client browser without updating it, or another approach that I can use where the cookie either gets update with any 'other' request, or some other "Spring way" of doing this that I am unaware of currently.
Not quite an answer, and this is a bit late for your question, but i am currently trying to solve this problem by looking at using websockets to contact the client pre-emptivly .
My current approach is to have a thread that checks the timeout every n minutes, and is reset whenever a endpoint is called (detecting endpoint calls as per this thread)
https://stackoverflow.com/a/22371901
when timer gets close to 0, use websockets to let the client know
Good overview here https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
We are using spring boot and angular, so this tutorial may also help, but it does have some angular specific stuff.
https://www.javaguides.net/2019/06/spring-boot-angular-8-websocket-example-tutorial.html
good luck if you are still trying at this.

Can I use Thread.sleep in a servlet to add random delays for the local server to answer my api call?

My deployed server has sometimes long response times, while working and developing at localhost all calls are really fast.
This has made my application enter unexpected behaviour once deployed a few times due to problems with resource loading taking too long.
I'd like to simulate in my local tests the bad connection with my real server, therefore I want to add a random delay to every request-response and my first thought was to use Thread.sleep in the servlet:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//add delay before processing request
if (DELAY > 0){
int delay;
if (RANDOMIZE){
delay = Random.nextInt(DELAY);
} else {
delay = DELAY;
}
try {
Thread.sleep(delay);
} catch (InterruptedException e1) {
logger.error(e1);
}
}
...
However I have read that one should not use Thread.sleep() inside a servlet, but the context of such discouragement and their solutions are drastically different from my case, can I use thread.sleep() in this context?
EDIT: This is of course only for local and for the client to be strained a bit in the local tests... I just want to simulate the bad network I've encountered in reality!
I think this whole approach is flawed. I wouldn't introduce a random delay (how are you going to repeat test cases?). You can introduce a Thread.sleep(), but I wouldn't. Would this be in your production code ? Is it configurable ? What happens if it's accidentlally turned on in production ?
I would rather set up a test server with the exact characteristics of your production environment. That way you can not only debug effectively, but build a regression test suite that will allow you to develop effectively, knowing how the application will perform in production.
Perhaps the one concession to the above is to introduce network delays (as appropriate) between client and server if your users are geographically diverse. That's often done using a hardware device on the network and wouldn't affect your code or configuration.
I did this to get delay :
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("<meta http-equiv=\"Refresh\" content=\"3;url=home.jsp/\">");
}
Remember that in content=\"3;url=home.jsp/\", 3 is the delay seconds and home.jsp is the page you want to go to after the given seconds.

Multiple Connectors on a Jetty Server

I'm trying to run a Jetty Server that can have a number of people connect to the server and see a list of print outs. I want everybody who connects to see the same values printed out.
For instance, if I have a single list keeping track of the time and I want 5 or so people to be able to go to my website (e.g. localhost:8080/time) and have them all see what time it is every 30 seconds, how would i set that up?
What I have:
I am using Jetty.
I created a single server bound to port 8080.
I created my own handler that extends AbstractHandler
this writes to the screen saying when an event has transpired (i.e. 30 seconds have passed)
If two people connect to this page, they each see a print out every minute (that is it switches back and forth letting each person know when every other event has transpired)
If 3 people connect, only two can stay connected and the third just spins getting no output to the screen
I have not set up an Connectors of my own since my attempts to do so have been unsuccessful and i'm not sure how I understand if that is the solution to my problem.
Any help would be much appreciated and if anybody has some idea but needs some clarification on what I am doing I would be glad to give more details.
Thanks!
Handler code:
#Override
public void handle(String target, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException
{
httpServletResponse.setContentType("text/html;charset=utf-8");
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
request.setContextPath("/time");
request.setHandled(true);
while (true) {
synchronized(Main.list) {
while (!Main.list.isEmpty()) {
Double time = Main.list.get(0);
httpServletResponse.getWriter().println("<h1>The time now is " + time + "</h1>");
httpServletResponse.flushBuffer();
Main.list.remove(0);
}
try {
Main.list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
So the list object is a static ArrayList defined in the Main class that I wake up (i.e. notify) every 30 seconds. Hopefully this helps someone understand more what I am talking about as i'm not sure what I could change in the handler...
How are you feeding clients into your handler? Browsers have limits to the number of connections are made to to a particular host, perhaps your seeing that.
there is nothing intrinsically wrong that handler code aside from it being a generally odd thing to see in a handler

Categories

Resources