If two threads are accessing this method on server, will it be thread safe? The threads are coming from GWT timer.
public UserDTO getUserFromSession()
{
UserDTO user = null;
HttpServletRequest httpServletRequest = this.getThreadLocalRequest();
HttpSession session = httpServletRequest.getSession();
Object userObj = session.getAttribute("user");
if (userObj != null && userObj instanceof UserDTO)
{
user = (UserDTO) userObj;
}
return user;
}
A method is thread safe if it doesn't access to external (to the method) shared variables.
The problem in your code could be on that line of code:
HttpServletRequest httpServletRequest = this.getThreadLocalRequest();
because this.getThreadLocalRequest() seems to access a shared variable.
To be sure post the whole class, but for what I can see it is not thread safe.
Also after the comment that explain what getThreadLocalRequest method returns a HttpServletRequest safely the code remains not thread safe.
Infact HttpSession is not thread safe according to this article: basically the session can change during the code execution.
For example you can return the user also after an invalidation of the session.
Imagine this steps:
thread 1 thread 2
---------------------------------------------- --------------
Object userObj = session.getAttribute("user");
session.invalidate();
if (userObj != null && userObj instanceof UserDTO) {
user = (UserDTO) userObj;
}
return user;
At the end you return a user also if the session was invalidated by another thread.
This method in of itself is harmless. It would be harmless even if you did not have a thread local request. The only problem with it I see is the off case in which you retrieve attribute "user" while it is instantiated, and another thread wipes attribute "user" clean before the first thread can exit the method. You'd be dealing with a user instance in one thread and in the other, you might be performing logic differently due to the fact that "user" attribute is no longer defined.
That said, I sincerely doubt that any problems would arise since these are all methods that read and don't write with no side effects. Just be mindful of the fact that several threads could be (and probably will be) handling the same instance of user so you'll want to keep thread-sensitive operations on user under a synchronized block in that case.
Yes, it is threadsafe as far as only your given method is concerned.
getThreadLocalRequest() is local to your current thread and getSession() is threadsafe as well.
Even getting the userObj from the session should not cause issues.
But after all multiple calls could access the same UserDTO object.
Therefore you need to make sure that either possible changes in this object are done in a threadsafe way or that the object is immutable.
the method looks threadsafe but it isn't, but in a more subtile way:
While getSession() and Session is safe, the session and its contents are not.
The Session you were looking for can go away anytime. It is not enough to examine only this method, but all other session dependent objects as well.
In a high load situation, you need to take care, that your getuser function will not recreate the session on the fly.
getSession(false) will take care of this. You will have a null check on the Session returned and abort your call in that case.
The user object as stated by others before is another responsibility.
Related
I am attempting to use synchronize method in spring controller. Because our Payment gateway hits method [#RequestMapping(value="/pay",method=RequestMethod.POST)] different transactions [txn id : txn01 & txn02] at a time. But these 2 different transaction processing one by one than parallel due to using synchronize block.
Problem -> Why i am using synchronize block in controller is that say Transaction [txn01] hits [#RequestMapping(value="/pay",method=RequestMethod.POST)] twice like duplicate call from payment gateway. before finishing first call [backend processing] i get second call from payment gateway for same tran id.
Is there any way to process two different transaction parallel with using transaction id in synchronize block other than duplicate call i mean same tran id. Please advice me.
Please let me know if my question is unclear.
#RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
synchronized (this) {
return this.processPayAck(httpRequest, httpResponse, session);
}
}
public synchronized String processPayAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
// Payment Acknowledgment process here
if (sametranIDNotExists) {
// first call here
callWS(); - processing business logic.
return someURL;
} else {
// Gets second call here before first call completed
return someURL;
}
}
Modified code :
Is it correct way to use intern inside synchronize block.
#RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
String tranID = httpRequest.getParameter("tranID");
synchronized (String.valueOf(tranID).intern()) {
return processPayAck(httpRequest, httpResponse, session);
}
}
I'm not sure if you are working in a distributed environment.
If there is only one machine, you can remove the syncronized keyword and create name-based locks with your transation id instead.
If this program is working in a cluster and there are multiple machines, which means the request might be assigned to different machine, I think you need to aquaire distribution-lock with Redis or other frameworks.
Synchronized block is used to provide thread safety. Also when multiple threads are trying to access same object, thread only with object level lock can acces synchronized(this) block. While one among a group of threads get object level lock, rest of the threads wait (Threads access synchronised block one by one but not in parallel).
Appropriate use : Use synchronized block when threads are trying modifying same resource(to avoid data inconsistancy). In this case threads are trying to modify same database resource. But as mentioned modifications are done on 2 different transactions(rows).
If modifying one row doesn't harm the other one then it is not required to use the line
return this.processPayAck(httpRequest, httpResponse, session);
within synchronised block. Instead it could be written as:
#RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
return this.processPayAck(httpRequest, httpResponse, session);
}
Suggestion : Use CopyOnWriteArrayList (as an instance variable not local variable) to store transaction id at the end of payAck method and use contains("textId") method to check whether the given transaction id is using payAck method again.
in my java project web.xml, session timeout parameter set 5 minutes. After 5 minutes, all session object kill or it remains memory?
I had the same question once so I checked out tomcat source code. I did not check everything but I will as much as i did. There is a StandardSession class which has many method tow of the methods are related to this.
one of them is expire() which updates isValid variable to false.
there is another method recycle() which clears all attributes and reset many properties like lastAccessedTime, maxInactiveInterval etc.
Now I assume that these methods are being called when the session is timed out. So it does not kill them it recycles them.
I went into session manager ManagerBase to see if that it what it does. Whenever new request comes the the below method is being called(there are few other method calls before this).
/**
* Get a session from the recycled ones or create a new empty one.
* The PersistentManager manager does not need to create session data
* because it reads it from the Store.
*/
#Override
public Session createEmptySession() {
return (getNewSession());
}
I'm using a custom interceptor which creates a new db connection, and sets this connection onto the current action before executing the action. After that, the interceptor closes the connection.
I'm looking for a convenient way to share this db connection with other classes / static methods (such as Models) that are used by the action. E.g so I can call static method like User.get( id ) or User.getEmail( id ) without having to pass the db connection to each method separately.
I could set the db connection onto the ActionContext from the interceptor, by doing:
ServletActionContext.getActionContext().put("db", db );
And then I could access this db connection from a static method, such as:
public class User implements Model
{
public static String getEmail(int id)
{
Connection db =
(Connection) ServletActionContext.getActionContext().get("db");
//...
}
}
My question is, would a new ActionContext be generated for every given request, so I can be sure that a new db connection will be used each time? E.g if I have 500 people visiting mysite.com/fooAction, could I be sure that each of those 500 requests is generating a unique ActionContext, and each call to User.getEmail() would access only the db connection which is unique to the given request?
Thanks.
To answer the question :
My question is, would a new ActionContext be generated for every given
request, so I can be sure that a new db connection will be used each
time?
Is yes. Reference is the java-doc. It similar to the one provided by Alfredo Osorio only it refers to 2.3.x version.
Can you say what struts2 version is being used?
I was not able to find any version that uses
ServletActionContext.getActionContext()
but instead the signature is
ServletActionContext.getActionContext(javax.servlet.http.HttpServletRequest)
To answer the comment regarding thread-local being static and still the ActionContext instance being unique per request its because the doing a
ActionContext.getContext()
internally invokes a get on the thread local instance.
actionContext.get()
You may find the following post helpful in this regard.
However to delve deeper, the method
ServletActionContext.getActionContext(javax.servlet.http.HttpServletRequest)
takes a different route than using the thread-local.
public static ActionContext getActionContext(HttpServletRequest req) {
ValueStack vs = getValueStack(req);
if (vs != null) {
return new ActionContext(vs.getContext());
} else {
return null;
}
}
public static ValueStack getValueStack(HttpServletRequest req) {
return (ValueStack) req.getAttribute(STRUTS_VALUESTACK_KEY);
}
getActionContext
getValueStack
Below are some additional references (source code).
ValueStack
OgnlValueStack
ActionContext
The following posts may also be helpful.
will-a-new-actioncontext-and-valuestack-be-created-for-every-new-action-object
struts2-actioncontext-and-valuestack
Update 2 :
Wanted to add as mentioned here (Link 1 above) that in case of ActionChaining being involved, the action is invoked with its own interceptor stack and result.
The thread in which its executed, however is the same.
The value-stack and parameters are copied over. See - ActionChainResult#execute(ActionInvocation).
Once the chain-invocation is complete, the state of the action-context is reset. (See DefaultActionProxy#execute() ).
Partial Information : Although the action-invocation is set in DefaultActionInvocation#init(ActionProxy) I was not able to determine if or where it is reset.
Sources :
DefaultActionInvocation
DefaultActionProxy
DefaultActionProxyFactory
ActionChainResult
FilterDispatcher(Deprecated)
My question is, would a new ActionContext be generated for every given
request, so I can be sure that a new db connection will be used each
time?
Since ActionContext uses ThreadLocal it is thread safe. Struts 2 creates an ActionContext for each request, and each request has its own thread. So yes, if you create a new connection and store it in the ActionContext every thread will have its own connection. But I don't recommend you to store the connection in the ActionContext because this couple you to Struts 2 which is not a good thing, also your services shouldn't be calling web specific classes because it also couple them.
From Struts 2 Javadoc:
The ActionContext is the context in which an Action is executed. Each
context is basically a container of objects an action needs for
execution like the session, parameters, locale, etc.
The ActionContext is thread local which means that values stored in
the ActionContext are unique per thread. See the
ActionContext.ActionContextThreadLocal class for more information. The
benefit of this is you don't need to worry about a user specific
action context, you just get it:
ActionContext context = ActionContext.getContext(); Finally, because
of the thread local usage you don't need to worry about making your
actions thread safe.
ActionContext excerpt:
public class ActionContext implements Serializable {
static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();
....
}
I had the problem, that every time i retrieved a collection from the gwt request factory, there was the "findEntity()"-method called for every entity in that collection. And this "findEntity()"-method calls the SQL-Database.
I found out that this happens because request factory checks the "liveness" of every entity in the "ServiceLayerDecorator.isLive()"-method (also described here: requestfactory and findEntity method in GWT)
So i provided my own RequestFactoryServlet:
public class MyCustomRequestFactoryServlet extends RequestFactoryServlet {
public MyCustomRequestFactoryServlet() {
super(new DefaultExceptionHandler(), new MyCustomServiceLayerDecorator());
}
}
And my own ServiceLayerDecorator:
public class MyCustomServiceLayerDecorator extends ServiceLayerDecorator {
/**
* This check does normally a lookup against the db for every element in a collection
* -> Therefore overridden
*/
#Override
public boolean isLive(Object domainObject) {
return true;
}
}
This works so far and I don't get this massive amount of queries against the database.
Now I am wondering if I will get some other issues with that? Or is there a better way to solve this?
RequestFactory expects a session-per-request pattern with the session guaranteeing a single instance per entity (i.e. using a cache).
The proper fix is to have isLive hit that cache, not the database. If you use JPA or JDO, they should do that for you for free. What matters is what "the request" thinks about it (if you issued a delete request, isLive should return false), not really what's exactly stored in the DB, taking into account what other users could have done concurrently.
That being said, isLive is only used for driving EntityProxyChange events on the client side, so if you don't use them, it shouldn't cause any problem unconditionally returning true like you do.
What is the difference between Session.getDefaultInstance(props, authenticator) and getInstance(props, authenticator)? In general, when will you choose one over the other?
I also read Java doc on getDefaultInstance(props, authenticator), but still couldn't able to make out the difference distinctly/clearly.
Hope experts can help me in understanding this better.
UPDATE: Actual reason that triggered to ask this question is: We've used Session.getDefaultInstance() method in some places within our web-based application. Sometimes, it throws java.lang.SecurityException: Access to default session denied, on quick googling, it suggested to use Session.getInstance() method instead. Hence, when one would choose one over the other?
If you read the documentation, you will see that
getDefaultInstance
Get the default Session object. If a default has not yet been setup, a new Session object is created and installed as the default.
Therefore, if one does not already exist, it call getInstance()
getInstance
Get a new Session object.
So, a new session object is created, regardless of whether one already exists.
FAQ says: https://javaee.github.io/javamail/FAQ#getdefaultinstance
Q: When should I use Session.getDefaultInstance and when should I
use Session.getInstance?
A: Almost all code should use Session.getInstance. The
Session.getDefaultInstance method creates a new Session the first
time it's called, using the Properties that are passed. Subsequent
calls will return that original Session and ignore any Properties you
pass in. If you want to create different Sessions with different
properties, Session.getDefaultInstance won't do that. If some other
code in the same JVM (e.g., in the same app server) has already
created the default Session with their properties, you may end up
using their Session and your properties will be ignored. This often
explains why your property settings seem to be ignored. Always use
Session.getInstance to avoid this problem.
Cause
This error is raised in the getDefaultInstance method in javax.mail.Session.java. According to this source code, this error occures when the default session object is already initialized, but authenticator instance is renewed or changed, or the class loader of the default session object is different from the argument authentificator's. Maybe the java source code using the default session instance of the java mail is recompiled and reloaded, or duplicate javamail class libraries are included into the Classpath of the environment.
it gives proper solution
javax.mail.Session.java file
public static synchronized Session getDefaultInstance(Properties props,
Authenticator authenticator) {
if (defaultSession == null)
defaultSession = new Session(props, authenticator);
else {
// have to check whether caller is allowed to see default session
if (defaultSession.authenticator == authenticator)
; // either same object or both null, either way OK
else if (defaultSession.authenticator != null &&
authenticator != null &&
defaultSession.authenticator.getClass().getClassLoader() ==
authenticator.getClass().getClassLoader())
; // both objects came from the same class loader, OK
else
// anything else is not allowed
throw new SecurityException("Access to default session denied");
}
return defaultSession;
}
For me, it was very important to use getInstance() instead of getDefaultInstance().
Because after mail session properties was changed, mail session still was storing old properties.
So getDefaultInstance() - it is looks like Singleton.
As docs said:
Note also that the Properties object is used only the first time this method is called, when a new Session object is created. Subsequent calls return the Session object that was created by the first call, and ignore the passed Properties object. Use the getInstance method to get a new Session object every time the method is called.