How to pass data between two endpoints in MVC Spring Controlller - java

I am quiet new in MVC and Spring and I want to learn best practices and solution so I decided to ask what is the best practice or how you would solve my problem.
I got controller with scope session (To store data in global variable not overrided when another user send request). I got global variable as I mentioned and two endpoints. First endpoint POST - here I send form and call another REST service to fetch data - call depends on data from form. Second endpoint GET - here i send which page to return. This endpoint is used for pagination.
Where is the problem? I have to store data in global variable because when I fetch data in POST endpoint I do not have access to it in GET endpoint. I do not like this solution. Do you have any ideas how to solve it better?
#Controller
#SessionScope
public class someController {
Global variable
#PostMapping(value = "/endpoint")
public String endpoint1(Form form){
//here I fetch data from another REST service depends on form data and save it to global variable
}
#GetMapping(value = "/anotherenpoint")
public String endpoint1(int page){
//here I get data from global variable and return to view as Page object
}

Your #Controller shouldn't be session scoped, instead make your variable session scoped. (you can also annotate it with #Autowired, so your controller automatically has the right variable for the current user)

Related

Spring MVC something like OptimisticLocking in the controller

I have a Spring controller and two methods that one prepare order data for user to edit, the other merage the latest order information change come back from the form (POST).
Now I want to make some procedure working like OptiMistic Locking without add version column in database. Because client take sometime to edit the order before session expires, I have to make the post method to be sure the order is still on the same status when client first open the order edit page
So I add a orderStatus attribute to carry the original status before order edit page is loaded and compare with the latest status from DB when save the order edit, like this:
#RequestMapping("/order")
#Controller
public class OrderController{
private String orderStatus;
#RequestMapping(value = "/{orderId}/edit", method = RequestMethod.GET)
public String prepareOrderEditForm(....){
....
// remember the original status
orderStatus = Order.getLatestStatus(orderId);
}
#RequestMapping(value = "/processEdit", method = RequestMethod.POST)
public String prepareOrderEditForm(....){
....
String currentOrderStatus = Order.getLatestStatus(orderId);
// compare most recent status with the original status
if(currentOrderStatus.equals(orderStatus) == false){
....//something is wrong, someone may be already edit the order
return "failed";
}else{
orderService.merge(order);
orderStatus = order.getNewStatus();
}
return "updated";
}
}
I understand this design is not as robust as Optimistic locking by database it may ignore the time when DML executed, also the orderStatus here should use getter/setter as java bean. So please exclude above concern but put the scope only within this controller
My question is, as Spring MVC is working in single thread like JSP Servlet, is there any problem while multiple user log on to the spring web application and edit the same order so orderStatus in this controller class somehow intertwined by multiple user(concurrency issue)?
Neither JSP nor Spring MVC are single threaded.
You should always try not to change instance/static variables in your controller methods.
Edit:
I should have mentioned that session thread safety will only work when you set Spring MVC abstract controller's synchronizeOnSession to true (by default it is false).
In your case you can:
1) set synchronizeOnSession to true in your spring application context where you define your controller.
2) Extend from Spring's AbstractController and override handleRequestInternal method.
3) in handleRequestInternal call request.getSession().setAttribute("orderStatus")

How to make a Model attribute global?

I'm using Spring MVC Framework and I'd like all the .jsp pages of the View to have access to the User's attributes(name, sex, age...). So far, I use the addAttribute method of the Model(UI) in every Controller to pass the current User's attributes to the View. Is there a way to do this only once and avoid having the same code in every Controller?
You can use Spring's #ControllerAdvice annotation on a new Controller class like this:
#ControllerAdvice
public class GlobalControllerAdvice {
#ModelAttribute("user")
public List<Exercice> populateUser() {
User user = /* Get your user from service or security context or elsewhere */;
return user;
}
}
The populateUser method will be executed on every request and since it has a #ModelAttribute annotation, the result of the method (the User object) will be put into the model for every request through the user name, it declared on the #ModelAttribute annotation.
Theefore the user will be available in your jsp using ${user} since that was the name given to the #ModelAttribute (example: #ModelAttribute("fooBar") -> ${fooBar} )
You can pass some arguments to the #ControllerAdvice annotation to specify which controllers are advised by this Global controller. For example:
#ControllerAdvice(assignableTypes={FooController.class,BarController.class})
or
#ControllerAdvice(basePackages={"foo.bar.web.admin","foo.bar.web.management"}))
If it is about User's attributes, you can bind the model bean to session as an attribute which can be accessed on every view. This needs to be done only once.
Another option could be is to implement a HandlerInterceptor, and expose the model to every request.

Fill a session bean with data before other methods execute in Controller

I'm creating a Spring application and in it I have a side bar which can be use to access modules of the application.
The side bar remains visible through out all views of the application. I create the view using Apache Tiles.
Since I don't want to hard code the menu items in the JSP, I want to populate required data(Link name, URL pattern) from database.
I want to do this only once so my plan is to create a session bean to put all these menu details in to it.
I create a Class called "MenuDAO" and I autowire it to the controller. Then I create a method to return that "MenuDAO" object and I mark it as a "ModelAttribute" in my controller.
#Autowired
private MenuDAO menuDAO;
#ModelAttribute("userDetails")
public UserDetail getUserDetail(){
return this.userDetails;
}
Now I am wandering where can I fill this DAO with data. I want to do it once per session(like when user logged in).
I cannot do it in the method with request mapping because once it get called Model attributes are already injected so only when the page loads next time the new data will be available.
Again I thought of doing it inside a method annotated with #PostConstruct. But then I need to do it in every Controller.
So I want to know where I can do this correctly so that I can populate this bean once per user session.
Check this question it is quite similar :
Dynamic content in all page in Spring MVC
You will have to populate your modelAndView in a postHandle method of an interceptor.
The interceptors are called for every request so it is your responsability to not call your dao for every request.
I would suggest you to store your menu in a cache instead of putting it in the session (using ehcache which is easy to set up in spring) .
I solve it as follows. I don't feel that this is the most convenient way. But it works.
I autowire session bean and mark required attribute as model attrbutes.
#Autowired
private MenuDAO menuDAO;
#ModelAttribute("menuDetails")
public List<ElabModuleWebProperties> getMenuDetails(){
return this.menuDAO.getMenuList();
}
After authenticate I redirect to url patter "home" by Spring Security. In that method I check whether the session bean has initialized and if not I do it there.
#RequestMapping(value = "home", method = RequestMethod.GET)
public String showHome(ModelMap model, Principal principal){
logger.debug("User accessing home");
if(!menuDAO.isMenuInitiationDone()){
logger.debug("menuDAO is empty, populating data for the object");
menuDAO.setMenuList(loginService.loadUserDetailsByUsername(SecurityContextHolder.getContext().getAuthentication().getName()));
menuDAO.setMenuInitiationDone(true);
}
model.put("menuDetails", menuDAO.getMenuList());
return "welcome";
}
In other controllers I can just autowire the bean and use it. No problem.

More than one user in the same Controller Spring MVC

I'm starting to develop for Web and I'm using Spring MVC as my Server Framework. Now I'm wondering about creating variables in Controller class. I had to do it to manage some data in server, but now I'm concerned about the following case: If I have more than one user sending information to the same page, at the same time, would one user interfere on another user variable?
Here's some code example:
#Controller
public Class myController {
int number;
#RequestMapping("/userInformation")
public String getInformation(int info) {
number = info;
}
public void doSomethingWithIt() {
number = number + 1;
}
}
In this case, If I have more than one user sending data to /userInformation at the same time, would Spring MVC create one Controller for each user? This way I wouldn't have problem, I guess. But if not, I have to rethink this implementation, don't I?
You are right. Controllers are singletons and must be stateless. Server side state belongs in session or in a data store. You can also use a request scoped object (look at bean scopes in spring).
The Spring container will create one instance of your Controller. So all users will share that instance.
If you have data that is private to a user, you have several options:
store it in the HTTP session (not recommended if it's a lot of data, as your memory usage might explode)
store it in a database and retrieve it upon each request, based on some property identifying the user
store it in a memory cache and retrieve it upon each request, based on some property identifying the user
Option 3 is the most simple one of them, you can even implement it as a Map<User, UserData> instance variable on your Controller if you like. It's not the cleanest, most beautiful or most secure option, just the most simple.
You should not use any instance variables in the Spring Controller that represent state of the controller class. It should not have state since its single instance. Instead you could have references to the injected managed beans.

Base controller, setting a private member variable, is this safe?

I want to create a base controller class that my other controllers will inherit from. I have a simple public api that takes the authentication token via the query string, so I want to do this:
public class MyBaseController {
private String token = "";
public MyBaseController() {
}
}
And then my real controller would be like:
#Controller
#RequestMapping("/api/users")
public class UserController extends MyBaseControler {
// controller methods here
}
My question is, how can i get access to the HttpServletRequest in my base controller, and get the querystring parameter "?token=abc123" value and set the token var with the value.
Is this thread safe? It is my understanding that there will be a new controller instance per request correct?
Your controllers are better off if they're stateless.
You can inject them with Spring services as needed, but I don't see any reason why they have to hang onto the value of the token as a member variable.
It's far more likely that you'll want to store the token in session scope. I think your idea is wrong-headed.
I'll point out that Spring itself has moved away from inheritance for controllers. They're all annotation-based now, with no common base class or interface. Why do you think devolving back to the design they abandoned is a good thing?
You don't need a base controller, either.

Categories