Struts2 and Spring integration thread safe - spring

We are using Struts2-Spring integration and all my action classes implement SessionAware, sample code to action class and their spring definition is given below,
public class IRXxxxAction extends ActionSupport implements SessionAware {
private Map session;
public String execute()
{//}
public void setSession(Map<String, Object> session)
{
this.session = session;
}
}
Spring Configuration
<bean name="userAction" class="com.IRXxxxAction" >
<property name="adminDAO" ref="adminDAO" />
</bean>
If I understand correctly, each auto-wired property will be a singleton, so if the above is true, is there anyway that the session map get shared between two simultaneous requests?
Regards,
Ayush

You have asked same question on the user mailing list and as said if you are using Struts2-Spring plugin make sure to put bean scope as prototype.
Struts2 create new instance of action on each request, since action work as a model also and in order to make it thread safe a new object is being created on each request and placed on value stack.
Not proving scope will be treated by Spring as singleton and for ever request same action instance will be given back which can leads to a lot of issue from data corruption to weird behavior.

Related

Common shared data objects for entire application

I have some data objects that are common across a Spring boot application - one is the logged in employee object and other is a category. I have created a #Component class which contains these are static variables. This way I do not even have to autowire them. They can be used directly like CurrentContext.employee in controllers.
#Component
public final class CurrentContext {
public static Category currentCategory;
public static Employee employee;
#Autowired
private CategoryService categoryService;
#Autowired
private EmployeeService employeeService;
#EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
currentCategory = categoryService.getCategory();
}
#EventListener
public void onLoginSuccess(InteractiveAuthenticationSuccessEvent event) {
employee = employeeService.getEmployeeByUserId(((MyUserDetails) event.getAuthentication().getPrincipal()).getUserId());
}
}
Is this a right way? Please suggest if there is a better way to handle shared data
Edit
Some background - I require the current logged in employee and a category which is common for all employees. So I autowired employeeService and categoryService in my controllers and use them to get the data. They are required in almost all my controller methods, so, I wanted to create a bean of these so that I directly use them in my controller and also save frequent database calls.
Normally, we only put the dependencies related to the cross-cutting concerns (i.e dependencies that are across the whole application such as security , logging , transaction stuff , time provider etc.) in the static field.
By accessing these kind of dependencies in the static way , we don't need to pass them through method parameters /constructors from object to object , which will make the API much cleaner without such noise (BTW. This is called Ambient Context Pattern in the .NET world).
Your Employee object most probably belong to this type , so it is ok to access it in a static way. But as their scope is per session , you cannot simply put it in the static field of a class. If yes, then you always get the same employee for all sessions. Instead, you have to somehow store it in an object which is session scope (e.g HttpSession) . Then at the beginning of handling a web request , you get it from the session and then put it in a ThreadLocal which is encapsulated inside a "ContextHolder" object. You then access that "ContextHolder" in a static way.
Sound very complicated and scary ? Don't worry as Spring Security has already implemented this stuff for you. What you need to do is to customize Authentication#getPrincipal()or extend default Authentication to contain your Employee. Then get it using SecurityContextHolder.getContext().getAuthentication()
For your currentCategory , if they are not the cross-cutting concerns and is the application scope , make a singleton bean to get it values is a much better OOP design.
#Component
public final class CurrentCategoryProvider {
#Autowired
private CategoryService categoryService;
public Category getCurrentCategory(){
//or cache the value to the an internal properties depending on your requirements
return categoryService.getCategory();
}
}
You then inject CurrentCategoryProvider to the bean that need to access currentCategory.

Spring scopes behaviour with respective to single and multiple web request/session

Whenever I try to analysis to understand the spring scopes I am stuck up somewhere. Below is my understanding from my analysis and before conclude myself I would like to confirm with you. Please correct me if my understanding is wrong.
<bean id="signupController" class="com.crp.controller.SignupController"
scope="">
If scope is "request", then for every new request from client irrespective of session, the spring container will generate new instance. Once the request is completes then spring container will manage to close the life cycle of instance.
If scope is "session", then for first request of a session a new instance will be generated by spring container and maintain it for all the client request for that particular session. Once the session timed out then spring container will manage to close the life cycle of instance.
If scope is "prototype", new instance will be generated by spring container whenever the bean is requested irrespective of session. Developer should manage the life cycle of the instance because spring container will not manage life cycle of prototype scope.
If scope is "singleton", only one instance generated by spring container and maintain it to be available for all request irrespective of sessions. For every session a copy of singleton instance will be maintained so that the one session singleton object will not share by another session and spring container will manage the life cycle of the copy of singleton instance and close it when session timed out.
Note: I believe most of you may have different opinion in my understanding on singleton scope. Even I am also confused in the behaviour of singleton scope and getting different information during my analysis. Please share your thoughts
Thank you.
For singleton scope how container works for below implementation for multiple user (session) send request at same time.
Login.java:
public class Login {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
LoginController.java:
public class LoginController extends MultiActionController {
private Login login;
public ModelAndView submitLogin(HttpServletRequest request,
HttpServletResponse response) {
String userName = request.getParameter("username");
String password= request.getParameter("password");
getLogin().setUserName(userName);
getLogin().setPassword(password);
// TODO send login bean to DAO for executing further business logic.
}
public Login getLogin() {
return login;
}
public void setLogin(Login login) {
this.login = login;
}
}
context.xml:
<bean id="login" class="com.crp.bean.Login" scope="singleton">
</bean>
<bean id="loginController" class="com.crp.controller.LoginController" scope="singleton">
<property name="login" ref="login"></property>
</bean>
No, that's not entirely correct.
If scope is "request", then for every new request from client irrespective of session, the spring container will generate new instance.
No. The bean instance will only be created if the code calls a method on the bean (i.e. on the scoped proxy that wraps the actual bean instance). But you're right that every request has a bean instance that is different from the other requests, and that the bean is destroyed at the end of the request (i.e. its life-cycle hooks, if they exist, are called when the request ends).
If scope is "session", then for first request of a session a new instance will be generated by spring container and maintain it for all the client request for that particular session.
Same remark as for the request scope: the bean instance is created on-demand.
If scope is "prototype", new instance will be generated by spring container whenever the bean is requested irrespective of session.
Right. But note that you need to request a bean instance to the spring context to get a new instance. If you inject a prototype to a singleton bean, the singleton bean will keep a reference to this bean for all of its life, and the same prototype bean will thus be used every time the singleton bean calls it.
If scope is "singleton", only one instance generated by spring container and maintain it to be available for all request irrespective of sessions. For every session a copy of singleton instance will be maintained so that the one session singleton object will not share by another session and spring container will manage the life cycle of the copy of singleton instance and close it when session timed out.
No. That is completely wrong. No copy is made at all. A single instance is created, injected everywhere, and used concurrently everywhere. The bean is only destroyed when the application itself ends.

Why is #PostConstruct method called after post/postback?

I have following request scoped Spring bean with #postconstruct method init():
#Component
#Scope("request")
public class editUserBB {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
private UserDto user;
#Autowired
private IUserService userService;
#PostConstruct
public void init() throws IOException {
String id_string = params.get("id");
Long id = Long.parseLong(id_string);
user = userService.getUserById(id);
}
public String save(){
// save to database
return "user?faces-redirect=true&id=" + (long)user.getId();
}
}
And userEdit.xhtml with h:form and commandButton:
<h:commandButton value="Save" action="#{editUserBB.save()}" />
However, after the save button is clicked the init() method is called once again annulling all the changes made to the UserDto object before I can save it to the DB. So what am I doing wrong?
And I just tested, the init() method is called even before save(), which I also don't understand..
That's not the fault of the init() method. That's just your own fault of placing the bean in the request scope instead of in the view scope.
A request scoped bean lives as long as a single HTTP request-response cycle. Opening the page with the form counts as one HTTP request. The HTTP request is garbaged when the associated HTTP response is finished sending the result to the client, including all associated request scoped beans. Submitting the form counts as another HTTP request which thus creates a completely new instance of the request scoped bean. If you explore/debug the instance's hashcode (and constructor), you'll notice that it are actually two physically completely distinct instances. There's thus absolutely no means of the init() to "override" the old values. The old values aren't there in first place.
JSF has solved this awkward behavior of a request scoped bean with introducing the view scope. You probably have ever heard/read about it before replacing JSF bean management facility by Spring's one for some reason. Spring bean management facility doesn't have a native concept of the view scope, you'd need to homegrow one.
If you intend to stick to Spring bean management facility, then your best bet is retaining the request parameter responsible for proper initialization of the data along with the form submit. You can use <f:param> for that:
<h:commandButton value="Save" action="#{editUserBB.save()}">
<f:param name="id" value="#{param.id}" />
</h:commandButton>
See also:
How to choose the right bean scope?
How do request/session/application work?
Unrelated to the concrete problem, you should avoid calling FacesContext during instance construction/initialization. This is bad design. In this particular case, move that line of obtaining the request parameter map to inside the init() method. Also here, JSF bean management facility has a standard solution in flavor of #ManagedProperty while Spring one doesn't have.

Using session attributes in spring MVC

I am developing a web application using spring MVC. I just want a simple example of how to do session management in this. I have seen lot of forums but I am not able to get a clear picture of this
My requirement is
I have an object, which I would like to be accessible in all controllers and JSP's I
would like to set that in the controller and get that in JSP
I am looking for something like
Session.setAtribute();
Could you please let me know a very simple instance . Thank you
There are different ways of accessing servlet session in Spring MVC. But I think this one is the one that best suits your problem. You can create a session scoped bean, which holds your desired info:
#Component("myObjectHolder")
#Scope(WebApplicationContext.SCOPE_SESSION)
public class MyObjectHolderImpl implements MyObjectHolder {
private long userId;
private String username;
private Theme theme;
// Getters & Setter
}
Then, you can access to it from other beans:
#Controller
public class MyController {
#Autowired private MyObjectHolder myObjectHolder;
#RequestMapping
public ModelAndView switchTheme(String themeId) {
...
Theme newTheme = themeService.get(themeId);
myObjectHolder.setTheme(newTheme);
...
}
}
You can access directly from your view too, but you must configure it:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
...
<property name="exposedContextBeanNames" value="myObjectHolder" />
</bean>
And in your JSP:
Hi ${myObjectHolder.username}, you switched
application theme to ${myObjectHolder.theme.name}
The simplest approach is to access HttpSession directly by injecting it into your handler method:
#RequestMapping("/page")
public ModelAndView page(HttpSession session) {
session.getAttribute("foo");
}

Struts2 - Session Troubles

I make (thanks with some users on this portal) my application that implements SessionAware.
This is my actual code :
public class UserManager extends ActionSupport implements SessionAware {
private Map<String, Object> session;
#Override
public String execute() throws Exception {
return SUCCESS;
}
public void setSession(Map<String, Object> map) {
this.session=map;
}
public String checkLogin() {
session.put("loggedOn", true);
return SUCCESS;
}
public String checkLogout() {
session.clear();
return SUCCESS;
}
}
And i check these variables on my .jsp :
<s:if test="#session['loggedOn']!=true">
DIV LOGIN
</s:if>
<s:else>
DIV LOGOUT
</s:else>
An easy piece of code.
What i'd like to know is this :
1 - the bean is (as default) request scoped. So when the request is finished it will be destroyed. But i see that, when i put a variable in the Map, it still alive on the server. How is possible? Is a variable of my Bean.
2 - Who call the setSession method? I think the servlet, due to the fact I implements that interface?
3 - I would like to detach about saving object/bean on a Session Object. I'd like to use the Bean session scoped (as for any kind of MVC framework). How can I do it on struts2?
Hope you can make clear these questions :) Cheers
1) Your bean is a struts2 action as such it is action scoped (which is a more restrictive scope than request). I say that action is a lesser scope because you can forward an action to another action in which case the previous action goes out of scope, request scoped objects however will stay in scope until the request is served. When you implement SessionAware you are provided with a reference to the SessionObject you are then putting your object into the Session object who's life span is much longer than your action. Clear your browser cache will remove the session value... you can also remove them by code be implementing SessionAware and removing the value from the map.
2 - The session already exists. You can get the session and add keys but even if you don't put anything in it, it will be there for use.
3 - You have a later question already for this topic see my answer there.

Resources