I have an HttpSessionListener. Is there a way, inside its sessionDestroyed method to distinguish between the following cases:
the session was destroyed because the session-timeout configured in the web.xml was exceeded
the session was destroyed programmatically by the the application calling HttpSession#invalidate
My use case is that I have a Single Sign On (SSO) arrangement between a number of applications and I want a global single sign off when one of the applications participating in the SSO arrangement explicitly logs off but not when its session times out, hence the need to distinguish between the two cases. I guess a way would be for the application to set some flag in the session object just prior to calling HttpSession#invalidate. The HttpSessionListener would then examine the session object and if that flag is found it would know this was a programmatic logout. If not, it was a container logout. Would that make sense and / or is there a better way?
You can use HttpSession#getLastAccessedTime() to obtain the timestamp of the last request sent by the client associated with the session. Then you can just do the math with help of HttpSession#getMaxInactiveInterval() and the current timestamp.
long lastAccessedTime = session.getLastAccessedTime();
long timeoutInMillis = TimeUnit.SECONDS.toMillis(session.getMaxInactiveInterval());
long now = System.currentTimeMillis();
boolean sessionHasBeenTimedout = (now - timeoutInMillis > lastAccessedTime);
// ...
Related
We appear to have a problem with MDriven generating the same ECO_ID for multiple objects. For the most part it seems to happen in conjunction with unexpected process shutdowns and/or server shutdowns, but it does also happen during normal activity.
Our system consists of one ASP.NET application and one WinForms application. The ASP.NET app is setup in IIS to use a single worker process. We have a mixture of WebForms and MVC, including ApiControllers. We're using a rather old version of the ECO packages: 7.0.0.10021. We're on VS 2017, target framework is 4.7.1.
We have it configured to use 64 bit integers for object id:s. Database is Firebird. SQL configuration is set to use ReadCommitted transaction isolation.
As far as I can tell we have configured EcoSpaceStrategyHandler with EcoSpaceStrategyHandler.SessionStateMode.Never, which should mean that EcoSpaces are not reused at all, right? (Why would I even use EcoSpaceStrategyHandler in this case, instead of just creating EcoSpace normally with the new keyword?)
We have created MasterController : Controller and MasterApiController : ApiController classes that we use for all our controllers. These have a EcoSpace property that simply does this:
if (ecoSpace == null)
{
if (ecoSpaceStrategyHandler == null)
ecoSpaceStrategyHandler = new EcoSpaceStrategyHandler(
EcoSpaceStrategyHandler.SessionStateMode.Never,
typeof(DiamondsEcoSpace),
null,
false
);
ecoSpace = (DiamondsEcoSpace)ecoSpaceStrategyHandler.GetEcoSpace();
}
return ecoSpace;
I.e. if no strategy handler has been created, create one specifying no pooling and no session state persisting of eco spaces. Then, if no ecospace has been fetched, fetch one from the strategy handler. Return the ecospace. Is this an acceptable approach? Why would it be better than simply doing this:
if (ecoSpace = null)
ecoSpace = new DiamondsEcoSpace();
return ecoSpace;
In aspx we have a master page that has an EcoSpaceManager. It has been configured to use a pool but SessionStateMode is Never. It has EnableViewState set to true. Is this acceptable? Does it mean that EcoSpaces will be pooled but inactivated between round trips?
It is possible that we receive multiple incoming API calls in tight succession, so that one API call hasn't been completed before the next one comes in. I assume that this means that multiple instances of MasterApiController can execute simultaneously but in separate threads. There may of course also be MasterController instances executing MVC requests and also the WinForms app may be running some batch job or other.
But as far as I understand id reservation is made at the beginning of any UpdateDatabase call, in this way:
update "ECO_ID" set "BOLD_ID" = "BOLD_ID" + :N;
select "BOLD_ID" from "ECO_ID";
If the returned value is K, this will reserve N new id:s ranging from K - N to K - 1. Using ReadCommitted transactions everywhere should ensure that the update locks the id data row, forcing any concurrent save operations to wait, then fetches the update result without interference from other transactions, then commits. At that point any other pending save operation can proceed with its own id reservation. I fail to see how this could result in the same ID being used for multiple objects.
I should note that it does seem like it sometimes produces id duplicates within one single UpdateDatabase, i.e. when saving a set of new related objects, some of them end up with the same id. I haven't really confirmed this though.
Any ideas what might be going on here? What should I look for?
The issue is most likely that you use ReadCommitted isolation.
This allows for 2 systems to simultaneously start a transaction, read the current value, increase the batch, and then save after each other.
You must use Serializable isolation for key generation; ie only read things not currently in a write operation.
MDriven use 2 settings for isolation level UpdateIsolationLevel and FetchIsolationLevel.
Set your UpdateIsolationLevel to Serializable
In web.config you can enable session compression.
<sessionState mode="InProc" customProvider="DefaultSessionProvider" compressionEnabled="true" >
What are positive and negative sides of this action?
Well, on the positive side, you need less space.
On the negative side, it needs time to compress, so it's slower.
Let me add, that in my opinion, if you use sessions at all, you've made an architectural mistake (exceptions my apply to this rule, but very very rarely).
It's not a good idea, because if a page writes something in a session, this gets overwritten if I simultanously open the same page in another browser window (it's the same session).
And because InProc sessions expire when you change something in the web.config file, you can create unlimited number of bugs for EVERY currently active user...
Plus you loose inProc sessions, if the VM gets moved to another server (cloud environments, failover, dynamic scaleOut).
Also, the InProc provider doesn't require objects to be marked as serializable.
If you change to, for example, an SQL session provider, you'll get exceptions in all places where you put an object that hasn't been marked as serializable into the session.
For example, when you need to query all the locations a user may access (according to portofolio rights in T_SYS_LocationRights):
You get the UserID from the formsAuth-cookie, then use it as the parameter:
DECLARE #userID integer
SET #userID = 12435
SELECT * FROM T_Locations
WHERE (1=1)
AND
(
(
SELECT ISNULL(MAX(CAST(T_SYS_LocationRights.LR_IsRead AS integer)), 0)
FROM T_SYS_LocationRights
INNER JOIN T_User_Groups
ON T_User_Groups.USRGRP_GRP = T_SYS_LocationRights.LR_GRANTEE_ID
WHERE T_SYS_LocationRights.LR_LC_UID = T_Locations.LC_UID
AND T_User_Groups.USRGRP_USR = #userID
) = 1
)
Don't just query something after the maxim:
if you'll ever need it, it's already there.
Design a web-application (which is multi-threaded by design) after that maxim, is a very bad idea.
If you don't need it, don't query it.
If you need it, query it.
If you needed it, don't store it in the session, it's better to query it again, if necessary
You can win much more time by executing all database operations at once, get all the data you need into a System.Data.DataSet (in one query-operation, one connection open-and-close), and then use that. When the page reloads, you can always reload the data (as a matter of fact, you even should).
Don't use the session as cache. It's not the cache
I check a session object and if it does exist then call another method which would use that object indirectly. Although the second method would access this object in a few nanoseconds I was thinking of a situation when the object exactly expires between two calls. Does Session object extends its lifetime on every read access from code for preventing such a problem ? If not how to solve the problem ?
If you are going to say why I don't pass the retrieved object from first method to second one, this is because I pass the ASP.NET Page object which carries many other parameters inside it to second method and if I try to pass each of them separately, there would be many parameters while I just pass one Page object now.
Don't worry, this won't happen
If I understand your situation it works sort of this way:
Access a certain page
If session is active it immediately redirects to the second page or executes a certain method on the first page.
Second page/method uses session
You're afraid that session will expire between execution of the first and second method/page.
Basically this is impossible since your session timer was reset when just before the first page starts processing. So if the first page had active session then your second page/method will have it as well (as long as processing finishes before 20 minutes - default session timeout duration).
How is Session processed
Session is processed by means of an HTTP Module that runs on every request and before page starts processing. This explains the behaviour. If you're not familiar with HTTP Modules, then I suggest you read a bit about IHttpModule interface.
It's quite difficult to understand your question, IMHO, but I will try.
From what I understand, you're doing something like:
string helloWorld = string.Empty;
if (this.Session["myObject"] == null)
{
// The object was removed from the session or the session expired.
helloWorld = this.CreateNewMyObject();
}
else
{
// Session still exists.
helloWorld = this.Session["myObject"].ToString(); // <- What if the session expired just now?
}
or
// What if the session existed here...
if (this.Session["myObject"] == null)
{
this.Session["myObject"] = this.CreateNewMyObject();
}
// ... but expired just there?
string helloWorld = this.Session["myObject"].ToString();
I thought that Session object is managed by the same thread as the page request, which would mean that it is safe to check if object exists, than use it without a try/catch.
I were wrong:
For Cache objects you have to be aware of the fact that you’re dealing essentially with an object accessed across multiple threads
Source: ASP.NET Cache and Session State Storage
I were also wrong about not reading to carefully the answer by Robert Koritnik, which, in fact, clearly answers the question.
In fact, you are warned about the fact that an object might be removed during page request. But since Session lifespan relies on page requests, it would mean that you must take in account the removal of session variables only if your request takes longer than the session timeout (see How is Session processed in the answer by Robert Koritnik).
Of course, such situation is very rare. But if in your case, you are pretty sure that the page request can take longer than 20 minutes (default session timeout), than yes, you must take in account that an object may be removed after you've checked if it exists, but before you really use it.
In this situation, you can obviously increment the session timeout, or use try/catch when accessing the session objects. But IMHO, if the page request takes dozens of minutes, you must consider other alternatives, as Windows services, to do the work.
I'm having difficulties understanding what the problem here is but let me try it again referring to thread safety.
Thread safety issue
If this is a thread safety issue, you can always issue a lock when creating a certain session object so other parallel requests won't run into a problem by double creating your object.
if (obj == null)
{
lock (objLock)
{
if (obj == null)
{
obj = GenerateYourObject();
}
}
}
Check lock documentation on MSDN if you've never used it before. And don't forget to check other web resources as well.
In the main thread I open a new thread that gets the number of new messages of user (takes about 5 secs) and this second thread should save the number in some place.
In the main thread I should check the "some place" and if the value exists I display it on the page.
Where can I save the value from the second thread to read it from the main one? This value is unique per user so I can't use static field.
Thank you for advance!
You can use static dictionary with user id as key and result as value. Protect dictionary access with locks. After main thread reads value, you can clear it from dictionary.
Use critical section to protect access to some data when several threads can read/write it. Use singleton instance to store data, global variable, registry pattern or whatever.
The way I do it, i have a vector od "ThreadData" elements.
Each started thread gets this element when started and it can update that data (protected by mutexes).
The main thread simply checks some flag in the element (ThreadState -- Running, Idle, Stopped, etc) and read the other data which the thread updated.
What is the difference between destroying a session and removing its values? Can you please provide an example demonstrating this?
I searched for this question, but don't grasp total answer. Some answers are:
Session.Abandon() destroys the session
Session.Clear() just removes all values
A friend told me this:
Clearing the session will not unset
the session, it still exists with the
same ID for the user but with the
values simply cleared.
Abandon will destroy the session
completely, meaning that you need to
begin a new session before you can
store any more values in the session
for that user.
The below code works and doesn't throw any exceptions.
Session.Abandon();
Session["tempKey1"] = "tempValue1";
When you Abandon() a Session, you (or
rather the user) will get a new
SessionId
When I test Session, it doesn't makes any change when I Abandon the session.
I just find one difference:
session.Abandon() raises Session_End event
Clear - Removes all keys and values from the session-state collection.
Abandon - removes all the objects stored in a Session. If you do not call the Abandon method explicitly, the server removes these objects and destroys the session when the session times out.
It also raises events like Session_End.
Session.Clear can be compared to removing all books from the shelf, while Session.Abandon is more like throwing away the whole shelf.
You say:
When I test Session, it doesn't makes any change when I Abandon the session.
This is correct while you are doing it within one request only.
On the next request the session will be different. But the session ID can be reused so that the id will remain the same.
If you will use Session.Clear you will have the same session in many requests.
Generally, in most cases you need to use Session.Clear.
You can use Session.Abandon if you are sure the user is going to leave your site.
So back to the differences:
Abandon raises Session_End request.
Clear removes items immidiately, Abandon does not.
Abandon releases the SessionState object and its items so it can ba garbage collected to free the resources. Clear keeps SessionState and resources associated with it.
When you Abandon() a Session, you (or rather the user) will get a new SessionId (on the next request).
When you Clear() a Session, all stored values are removed, but the SessionId stays intact.
This is sort of covered by the various responses above, but the first time I read this article I missed an important fact, which led to a minor bug in my code...
Session.Clear() will CLEAR the values of all the keys but will NOT cause the session end event to fire.
Session.Abandon() will NOT clear the values on the current request. IF another page is requested, the values will be gone for that one. However, abandon WILL throw the event.
So, in my case (and perhaps in yours?), I needed Clear() followed by Abandon().
this code works and dont throw any exception:
Session.Abandon();
Session["tempKey1"] = "tempValue1";
It's because when the Abandon method is called, the current Session object is queued for deletion but is not actually deleted until all of the script commands on the current page have been processed. This means that you can access variables stored in the Session object on the same page as the call to the Abandon method but not in any subsequent Web pages.
For example, in the following script, the third line prints the value Mary. This is because the Session object is not destroyed until the server has finished processing the script.
<%
Session.Abandon
Session("MyName") = "Mary"
Reponse.Write(Session("MyName"))
%>
If you access the variable MyName on a subsequent Web page, it is empty. This is because MyName was destroyed with the previous Session object when the page containing the previous example finished processing.
from MSDN Session.Abandon
Session.Abandon()
will destroy/kill the entire session.
Session.Clear()
removes/clears the session data (i.e. the keys and values from the current session) but the session will be alive.
Compare to Session.Abandon() method, Session.Clear() doesn't create the new session, it just make all variables in the session to NULL.
Session ID will remain same in both the cases, as long as the browser is not closed.
Session.RemoveAll()
It removes all keys and values from the session-state collection.
Session.Remove()
It deletes an item from the session-state collection.
Session.RemoveAt()
It deletes an item at a specified index from the session-state collection.
Session.TimeOut()
This property specifies the time-out period assigned to the Session object for the application. (the time will be specified in minutes).
If the user does not refresh or request a page within the time-out period, then the session ends.
Clearing a session removes the values that were stored there, but you still can add new ones there. After destroying the session you cannot add new values there.
clear-its remove key or values from session state collection..
abandon-its remove or deleted session objects from session..
Existence of sessionid can cause the session fixation attack that is one of the point in PCI compliance. To remove the sessionid and overcome the session fixation attack, read this solution - How to avoid the Session fixation vulnerability in ASP.NET?.
I think it would be handy to use Session.Clear() rather than using Session.Abandon().
Because the values still exist in session after calling later but are removed after calling the former.
this code works and dont throw any exception:
Session.Abandon();
Session["tempKey1"] = "tempValue1";
One thing to note here that Session.Clear remove items immediately but Session.Abandon marks the session to be abandoned at the end of the current request. That simply means that suppose you tried to access value in code just after the session.abandon command was executed, it will be still there. So do not get confused if your code is just not working even after issuing session.abandon command and immediately doing some logic with the session.