I have an application in which users frequently leave the page running in their browser all day. The page has an intervalrendered that updates data every 30 seconds.
However, if the user doesn't interact with the page for a while, it still expires. I'd like it to auto-extend for as long as the browser is open and making the scheduled requests.
Is there a way to automatically extend the session everytime that the scheduled renderer is fired for one of these pages?
I don't really want to do Javascript hacks to click buttons when my code is already being called every 30 seconds by ICEFaces. The following hacked workaround for ICEFaces internal timer seems to work:
private void updateSessionExpiration () {
HttpSession sess = getSession();
if (sess != null) {
try {
Field fld = SessionDispatcher.class.getDeclaredField("SessionMonitors");
fld.setAccessible(true);
Map map = (Map)fld.get(null);
String sessID = sess.getId();
if (map.containsKey(sessID)) {
log.info("About to touch session...");
SessionDispatcher.Monitor mon =
(SessionDispatcher.Monitor)map.get(sessID);
mon.touchSession();
}
} catch (Exception e) {
log.error("Failed to touch session");
}
}
}
Also, with ICEFaces 1.8.2RC1 (and presumably eventually with the release version of ICEFaces 1.8.2 as well), there are two new workarounds available:
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
if (session != null) {
SessionDispatcher.Monitor.lookupSessionMonitor(session).touchSession();
}
Or, put this in the web.xml to update on any hit to URLs within a specific pattern:
<filter>
<filter-name>Touch Session</filter-name>
<filter-class>com.icesoft.faces.webapp.http.servlet.TouchSessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Touch Session</filter-name>
<url-pattern>/seam/remoting/*</url-pattern>
</filter-mapping>
ICEfaces has an internal mechanism for keeping sessions alive (the mechanism is sometimes referred to as "heartbeating"). You may have a look at the belonging documentation and configure the mentioned parameters (especially heartbeatInterval and heartbeatTimeout) in your web.xml.
Iceface's Ajax bridge has callback APIs, for example Ice.onSessionExpired() which you may be able to use.
<body id="document:body">
<script type="text/javascript">
Ice.onSessionExpired('document:body', function() {
<!-- Do something here -->
});
</script>
See http://www.icefaces.org/docs/v1_8_0/htmlguide/devguide/references4.html#1094786 for more details.
If I have undestood what is your real purpose, then you need a sort of timer which automatically extends every user session client side, consequentely overriding the server side default page session expiration timing ( usually 30 minutes, only for detail because I understood that the script "renders" every 30 seconds if the user is active on the page ).
If so well, before JSF I always extended inactive user sessions via underground Javascript, so I'll use it also in your case. Pratically you can build a simple page listener in Javascript starting from:
window.setInterval( expression , msecIntervalTiming );
the "expression" can be a called function in which you invoke some dummy JSF needed to keep the session alive without reloading the current page visited by the user, in the past I used standard frame or iframe to make http calls, now with XHR / Ajax is more simple too.
Javascript example:
var timerID = null;
function simplePageListener() { // invoked by some user event denoting his absence status
// here goes all events and logic you need and know for firing the poller...
if (timerID == null) // avoid duplicates
timerID = window.setInterval( "window.startPoller()", msecIntervalTiming );
}
//...
function pollerStart() {
// make iframe or ajax/xhr requests
}
function pollerStop() {
// make action like page reloading or other needings
}
//...
function triggeredCleanTimer(timer) { // invoked by some user event denoting his active status
clearTimeout(timer); // it can be also the global variable "timerID"
timerID = null;
}
Substantially you use the "timerID" as a global reference to keep track of the listener status, so when you need to activate the "autoextension" you assign it a setInterval. Instead when the user come back to the page (triggered by some event you know), you clear the timeout stopping the listener polling. The above example obviously implies that the user, when comes back, must reload the page manually.
However in your specifical case, I'll avoid to interfere with javascript automatically generated by Icefaces framework. Even if, for ipothesys, you could simulate periodically some user events on invisible input elements (style set on "visibility: hidden", absolutely not on "display: none"), this causes the Icefaces event to not stop it and making itself work continuosly
Elements like <input type="button" name="invisivleButton" value="..." style="visibility: hidden, z-index: 0;" /> on which you can call periodically invoke the click event by
document.forms["YourDummyForm"]["invisivleButton"].click();
For the usage, see the old great JS docs of Devedge Online :-)
http://devedge-temp.mozilla.org/library/manuals/2000/javascript/1.3/reference/window.html#1203669
Related
I have a portlet. When the portlet loads, then before the first view is rendered, in some cases there is a need to call a repository which changes data in the database. I wouldn't go into more detail about why this is necessary and answers about this being a design flaw are not helpful. I am aware that it is a design flaw but I would still like to find out an alternative solution to the following problem:
The problem with this set-up is, that browsers send preloading requests. For example the URL of the page where the portlet resides is /test-portlet. Now when you type it in your address-bar then if you have it in your browser history, then the browser sends a GET request to the page already when it suggests it to you. If you press enter before the first GET request is resolved, then the browser sends a new GET request. This means that the portlet receives 2 separate requests which it starts to process parallelly. The first database procedure might work correctly but considering the nature of the database procedure, the second call usually gives an exception.
What would be a nice clean way to deal with the aforementioned problem from the Java application?
Sidenote: I am using Spring MVC.
A simple example of a possible controller:
#RequestMapping
public String index( Model model, RenderRequest request ){
String username = dummyRepository.changeSomeData(request.getAttribute("userId"));
model.add("userName", username);
return "view";
}
I would be interested in a solution to block the first execution altogether. For example somekind of a redirect to POST from controller which the browser wouldn't trigger. Not sure if it is achievable though.
Using locks I think you could solve it, making the secound request wait for the first to finish and then processing it. I don't have experience with locks in java but i found another stack exchange post about file locks in jave:
How can I lock a file using java (if possible)
Please refer to this answer, it might help you to detect and ignore some preloading requests. However you should also make sure the 'worst case' works, perhaps using the locking as suggested by #jpeg, but it could be as easy as using a synchronize block somewhere.
Since I don't see that chrome adds some specific header (or anyhow notifies the server about prerendering state) it is probably not possible to detect it on the server side... at least not directly. You can however simulate the detection on client side and later combine it with server call.
Notice that you can detect prerendering on the client side:
if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') {
// prerendering takes place
}
Now, you can break preloading on client side by showing alert box in case browser is in preloading state (or you can probably do the same with just some error in javascript, instead of using alert()):
if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') {
alert('this is alert during prerendering..')
}
Now when chrome prerenders the page it will fail because the javascript alert will prevent the browser to continue executing javascript.
If you type in chrome: chrome://net-internals/#prerender you can track when and for which pages chrome executes prerendering. In case of above example (with alert box during prerendering) you can see there:
Link Rel Prerender (cross
domain) http://some.url.which.is.preloaded Javascript
Alert 2015-06-07 19:26:18.758
The final state - Javascript Alret proves that chrome failed to preload the page (I have tested this).
Now how can this solve your issue? Well, you can combine this with asynchronous call (AJAX) and load some content (from another url) depending on wheater the page is actually prerendering or not.
Consider following code (which might be rendered by your portlet under url /test-portlet):
<html>
<body>
<div id="content"></div>
<script>
if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') {
// when chrome uses prerendering we block the request with alert
alert('this is alert during prerendering..');
} else {
// in case no prerendering takes place we load the actual content asynchronously
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// when the content is loaded we place the html inside "content" div
document.getElementById('content').innerHTML = xhr.responseText;
}
}
xhr.open('GET', '/hidden-portlet', true); // we call the actual portlet
xhr.send(null);
}
</script>
</body>
</html>
As you see the /hidden-portlet is only loaded in case browser is loading the page normally (without preloading). The server side handler under url /hidden-portlet (which can be another portlet/servlet) contains actual code which should not be executed during prerendering. So it is the /hidden-portlet which executes
dummyRepository.changeSomeData(request.getAttribute("userId"));
This portlet can also return normal view (rendered html) which will be asynchronously placed on the page under url /test-portlet thanks to the trick on /test-portlet: document.getElementById('content').innerHTML = xhr.responseText;.
So to sumarize the portlet under address /test-portlet only returns html with a javascript code which triggers actual portlet.
If you have many fragile portlets, you can go with this even further, so you can parametrize you /test-portlet with request parameter like /test-portlet?actualUrl=hidden-portlet so that address of the actual portlet is taken from url (which can be read as request parameter on server side). Server will in this case dynamically render the url which should be loaded:
So instead of hardcoded:
xhr.open('GET', '/hidden-portlet', true);
you will have
xhr.open('GET', '/THIS_IS_DYNAMICALLY_REPLACED_EITHER_ON_SERVER_OR_CLIENT_SIDE_WITH_THE_ADDRES_FROM_URL', true);
I wanted to know how request objects behave when a session is time out.
To be more specific I came across to one scenario, for which I am not able to figure out what is happening exactly.
The scenario is like this,
I have a login page for my web application with username and password fields. I have set the time out to 10 minutes for my app.
I am on the login page doing nothing for 15 minutes, so the session is timed out.
Now on the login I put the user name and password and hit submit. The page is getting refreshed instead of submitting.
So can I say upon session time out,the request object also times out?
Since you do not add any code to your question, my explanations should be taken with a grain of salt.
IMHO, you are starting the session when a client requests the login page (before he submits the login page) and also you've set it so that a request belonging to a timeout session will be redirected to login page.
So if the result is not to your liking you have to change some of the above.
But again, for a better answer, you have to show us some code.
And for your question about request time out. No it did not time out. It only times out if the server does not respond in time (which is a different kind of time out)
I added some Java code partials which I am using to direct requests belonging to sessions which are timed out to Login page. By the way I also should add that requests which did not require a session is handled before this redirection.
HttpSession session = request.getSession(false);
boolean hasActiveSession;
if (session == null) {
hasActiveSession = false;
//...
}
//...
if (!hasActiveSession) {
request.setAttribute("alert","Your session has timed out");
request.getRequestDispatcher("/WEB-INF/Login.jsp").forward(request, response);
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have created web application that handle user login with it and update logout time when user logout from application, but when user close web browser directly or system gone shutdown with some problem, i am not able to update user logout time.
Please give any possible way to update user logout time on such circumstances.
Assuming javascript on client side as it is a webapp.
In such cases you should send a request in browser close event
window.onbeforeunload = function(event) {
//send request to server .
}
Prefer to read :want to detect browser close event?
May the following steps help you to update logout time.
1.Keep updating a timestamp variable in session for each request.
2.During session time out get the variable value (which holds when user accessed at last) and update in logout record.
This could help without depending the browser to send logout request.
try this code to implement
window.onbeforeunload = function(event) {
var isOK = confirm("Are you sure to Leave this Page?");
if(isOK)
{
// try ajax for update your table
}
}
You can create a class which implements HttpSessionListener and annotated #WebListener() like this:
#WebListener()
public class MyListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent se) {
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
Date lougOutDate=new java.util.Date();
}
}
in sessionDestroyed method you retrieve the date of disconnection
Why don't you use a TimeOut ?
There is several solutions :
Timeout
Implement HTTP COOKIE. Check the link below
http://en.wikipedia.org/wiki/HTTP_cookie
In your specific case it should be Session Cookie
Session cookie
A user's session cookie[14] (also known as an in-memory cookie or transient cookie) for a website exists in temporary memory only while the user is reading and navigating the website. When an expiry date or validity interval is not set at cookie creation time, a session cookie is created. Web browsers normally delete session cookies when the user closes the browser.[15][16]
Do the stuff with Javascript as suggested
Hope it's help :)
You could try to write some Javascript that sends an "User logging out" message to the server. This code should be triggered using $.unload() (if you're using jQuery) or binding to the native unloadevent of the browser.
There is no way of precisely getting the logout time in such circumstances, as this might be for example caused by internet connection loss.
Some possibilities:
Send an ajax request on document unload to notify your server (as suggested by #Baadshah and #mthmulders)
Add a session timeout listener on the server to set the logout time when the session times out - this way even if the ajax doesn't get to the server you will know that the user logged out during the last few minutes (depending on the session duration)
Use the event beforeunload with jquery on your page.
The beforeunload event fires whenever the user leaves your page for any reason.
For example, it will be fired if the user submits a form, clicks a link, closes the window (or tab), or goes to a new page using the address bar, search box, or a bookmark.
You could exclude form submissions and hyperlinks (except from other frames) with the following code:
var inFormOrLink = false;
$(document).on('click','a', function() { inFormOrLink = true; });
$(document).bind('submit','form', function() { inFormOrLink = true; });
$(window).on('beforeunload',document, function(eventObject) {
var returnValue = undefined;
if (inFormOrLink == false) {
//do your action
}
});
EDIT: Answer found here: How to capture the browser window close event?
I've been playing with the Wicket autocompletetextfield. It has one problem though - when the session times out it stops working if the page itself isn't refreshed. This would be quite confusing for a customer I think, and I guess that it's not meant to work that way. Therefore, how can I make the Wicket autocompletetextfield work even though the session has timed out (and without refreshing the page)
To try it yourself:
Go to http://www.wicket-library.com/wicket-examples-6.0.x/ajax/autocomplete
Write something in the textfield, eg. bel
Wait for 5 minutes (I think that's is the default session timeout they use in the examples) and try again, without refreshing
the page. Now you'll only get a blank textfield.
I think you can't.
The only workaround I know is to set the Ajax error handling strategy to REDIRECT_TO_ERROR_PAGE, evaluate the Referer-Field in the HTTP header in the Error Page and provide a link (or auto redirect) to the page where the timeout occurred.
YourWicketApplication.java
#Override
public void init() {
super.init();
// ...
getExceptionSettings().setAjaxErrorHandlingStrategy(IExceptionSettings.AjaxErrorStrategy.REDIRECT_TO_ERROR_PAGE);
}
YourErrorPage.java:
public YourErrorPage(...) {
// ...
WebRequest request = (WebRequest) getRequest();
String referer = request.getHeader("Referer"));
// ... provide a link/auto redirect to this address
}
When a user logs in to my web app, I create a session:
session.setAttribute("SessionNumber","100");
And his username is added to a table named ONLINE_USERS.
Other Online users will be able to see him, they see all online users
When the user clicks on the log out button, I delete that row from the table, then I delete the session using:
session.invalidate();
But let's say the user existed the browser, his session will be gone, but the row will stay in the database as an online user, how to avoid this?
I'm using JSP-Servlets on Netbeans.
You can enable a custom HttpSessionListener to delete the table row upon session invalidation.
public class YourHttpSessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
//put row in the database
}
public void sessionDestroyed(HttpSessionEvent event) {
//delete the row from database
}
}
Declare the listener in your web.xml:
<listener>
<listener-class>YourHttpSessionListener</listener-class>
</listener>
Note that there will be a delay between the moment the user exits the browser and his session expires on the server. But session expiration time is configurable. You should find a suitable expiration timeout: not too long so you don't display for too much offline users as online, but not too short to allow connected users an idle period.
I think this is a good trade off for a chat application developed with basic servlet and jsp technology.
As I understand you want see users that are operating on web site at the moment, problem with HttpSessionListener is that session can live quite long before its destroyed, so it can happen that the user is not using the web site long time when it is destroyed.
(see http://www.smartsoftwarebits.com/qaa/46-misc/82-how-to-set-session-timeout-for-tomcat )
Solution: You can add a column to the database where you will store the time stamp of the last request
which user made. To keep this column up to date use a servlet filter. ( http://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html )
To clear online users add a timer job (for example using quartz) where you will delete rows (online users) that are older than (for example) 5 minutes (thus when last interaction is older than 5 mins.) ( http://quartz-scheduler.org/ )
Using this you will now quite precisely if there is user is "still there" or not.
In addition you can add a timer to client side javascript to make an ajax call periodically. You can handle this way the situation when user did not close the browser just were inactive for a while.
First thing is to catch the event when the browser is closed
You can try below code snippet in your jsp to hit a js function which will call an ajax function to hit server side component. Then simply use the session API to invalidate the session and add the code to delete the record from the table.
window.onbeforeunload = WindowClose;
function WindowClose() {
//Write a AJAx request here to hit the server side servlet to invalidate the session
}
Or use
<body onunload="WindowClose(); >
In the server side code , use
HttpSession session = request.getsession();
session.setMaxInactiveInterval(0); //or session.invalidate();
It will be good approach to define default session timeout value in the web.xml so that incase browser crashes, sessions will invalidate after the stipulated amount of time has passed.
<session-config>
<session-timeout>30</session-timeout>
</session-config>