Spring #SessionAttribute how to retrieve the session object in same controller - java

I am using Spring 3.2.0 MVC. In that I have to store one object to session.
Currently I am using HttpSession set and get attribute to store and retrieve the value.
It returns only the String not Object. I want to use #SessionAttribute when I tried it sets the object in session but I could not retrieve the session object
#RequestMapping(value = "/sample-login", method = RequestMethod.POST)
public String getLoginClient(HttpServletRequest request,ModelMap modelMap) {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
User user = sample.createClient(userName, password);
modelMap.addAttribute("userObject", user);
return "user";
}
#RequestMapping(value = "/user-byName", method = RequestMethod.GET)
public
#ResponseBody
String getUserByName(HttpServletRequest request,#ModelAttribute User user) {
String fas= user.toString();
return fas;
}
Both methods are in same controller. How would I use this to retrieve the object?

#SessionAttributes annotation are used on the class level to :
Mark a model attribute should be persisted to HttpSession after handler methods are executed
Populate your model with previously saved object from HttpSession before handler methods are executed -- if one do exists
So you can use it alongside your #ModelAttribute annotation like in this example:
#Controller
#RequestMapping("/counter")
#SessionAttributes("mycounter")
public class CounterController {
// Checks if there's a model attribute 'mycounter', if not create a new one.
// Since 'mycounter' is labelled as session attribute it will be persisted to
// HttpSession
#RequestMapping(method = GET)
public String get(Model model) {
if(!model.containsAttribute("mycounter")) {
model.addAttribute("mycounter", new MyCounter(0));
}
return "counter";
}
// Obtain 'mycounter' object for this user's session and increment it
#RequestMapping(method = POST)
public String post(#ModelAttribute("mycounter") MyCounter myCounter) {
myCounter.increment();
return "redirect:/counter";
}
}
Also don't forget common noobie pitfall: make sure you make your session objects Serializable.

Related

Spring MVC Accessing Session Attribute in Function in Controller Class

I was having some problem when trying to access session attribute in Controller. I declared my session attributes as such:
#Controller
#SessionAttributes({ WebKeys.OBJECT_SIX, WebKeys.DSP_LOGIC, WebKeys.NEW_CARD_FORM })
In each of my API, I am calling the function:
#RequestMapping(value = "/apiA.do", method = RequestMethod.POST)
public String doAPIa(Model model) {
setInfo(model);
}
#RequestMapping(value = "/apiB.do", method = RequestMethod.POST)
public String doAPIb(Model model) {
setInfo(model);
}
In my setInfo(), I am trying to access the session attribute and add value back to the model:
private void setInfo(Model model) throws Exception{
String populationId = // need to get from WebKeys.OBJECT_SIX session attribute
if(populationId!=null && (populationId.equals(Constants.POP_TYPE_ID))){
DisplayHelperTO helper = (DisplayHelperTO) // need to get from WebKeys.DSP_LOGIC;
NewCardNewBasicForm newCardForm = (NewCardNewBasicForm ) // need to get from WebKeys.NEW_CARD_FORM);
model.addAttribute("newCardForm", newCardForm);
model.addAttribute("dspLogic", helper);
}
}
I tried to declare in such way:
private void setInfo(Model model,
#SessionAttribute(WebKeys.OBJECT_SIX) String populationId) throws Exception{
}
However, if I am declaring the function in this way, the part to call the setInfo() in both doAPIa and doAPIb will be highlighted in syntax error. Any ideas on how to access the session attributes in function? Thanks!

Spring MVC forward to new URL keeping existing

I am trying to redirect to a previous page is a domain object is invalid. For example with a domain object of Address.
#Autowired
private Address address;
#RequestMapping(method = RequestMethod.GET)
public String addressPage(
ModelMap model
) throws StdAddFault {
model.addAttribute("address", address);
return TILE_GET_STARTED_ADDRESS;
}
#RequestMapping(value = "/validate", method = RequestMethod.POST)
public String selectAddress(
#Valid Address address,
BindingResult result,
Model model,
HttpSession session
) throws StdAddFault {
if(result.hasErrors()) {
model.addAttribute("address", address);
return "/address";
}
...
}
Problem is after the request instead of being /address it is /address/validate. This seems a little misleading but if i use a redirect or a forward i lose my information.
Is there any way to make the url after the contoller /address (for invalid data only)?
Update
I confirmed the above does not work. This does work but I am concerned one request may overwrite another's Address. Is this a valid concern?
if(result.hasErrors()) {
this.address = address;
return "redirect:/get-started/address";
}
I think what you're after is an "address" model attribute stored in session. This will retain unique address object per user-session
#Controller
#SessionAttributes({"address"})
#RequestMapping("/address")
public class AddressController {
/**
* Injected model reference will contain 'address' if it exists in session
*/
#RequestMapping(method = RequestMethod.GET)
public String addressPage(Model model) {
if(!model.containsAttribute("address"))
model.addAttribute("address", new Address());
return "address";
}
/**
* This handler maps to form selecting / updating the address object
* at the end of this method the address object is automatically
* stored in session
*/
#RequestMapping(value = "/validate", method = RequestMethod.POST)
public String selectAddress(#ModelAttribute("address") Address address) {
// do stuff with the newly updated address..
}
}
If in any other handler you need the address object, all you need to do is just inject #ModelAttribute("address") Address address on the argument
I can do it one of 2 ways but I am not sure which one to use..
1.)
if(result.hasErrors()) {
this.address=address;
return "redirect:/get-started/address";
}
My concerns here revolve around object lifecycle using the Autowired. Could one call overwrite the address on another?
The other option is to use session...
private String getMainPage(
ModelMap model,
HttpSession session
){
if(session.getAttribute("address") == null){
session.setAttribute("address", new Address());
}
model.addAttribute("mainNav", MAIN_NAV);
model.addAttribute("subNav", SUB_NAV);
model.addAttribute("address", session.getAttribute("address"));
return TILE_GET_STARTED_ADDRESS;
}
I would really like to avoid the session though.
Ok to get a new Object each time make your Controller implement BeanFactoryAware.
Then implement the method setBeanFactory something like this.
private Address address.
public void setBeanFactory(BeanFactory context) {
address = (Address)context.getBean("myAddress");
}
So everytime you call setBeanFactory this will get an Address object and because of the scope = prototype it will be a new object.
This is the answer I finally went with
#Controller
#RequestMapping("/address")
public class AddressController {
private String getMainPage(
ModelMap model,
HttpSession session
){
if(!model.containsAttribute("address"))
model.addAttribute("address", new Address());
...
return TILE_GET_STARTED_ADDRESS;
}
#RequestMapping(value = URL_ADDRESS_SELECT, method = RequestMethod.POST)
public String validateAddress(
#Valid Address address,
BindingResult result,
Model model,
RedirectAttributes attr,
HttpSession session
) throws StdAddFault {
if(result.hasErrors()) {
attr.addFlashAttribute("org.springframework.validation.BindingResult.address", result);
attr.addFlashAttribute("address", address);
return "redirect:/address";
}
....
}
}
This question + you guys helped me out a bunch!

how to use attribute value from one method to another method

i hav a home controller within which i hav 2 methods one is
#RequestMapping(value = "/mypage.te", method = RequestMethod.GET)
public String mypage1(Locale locale, Model model){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName(); //get logged in username
model.addAttribute("username", name);
model.addAttribute("customGroup",grpDao.fetchCustomGroup());
model.addAttribute("serverTime", formattedDate);
model.addAttribute("username", name);
return "mypage";
}
here in this method actually i call grpDao.fetchCustomGroup() method from a Dao class which performs a native query and fetches data and returns and it is saved in customGroup.
now the same fetchcustomGroup() method is to be used in another method i.e
#RequestMapping(value = "/manageGrps.te", method = RequestMethod.GET)
public String man_grp_connections(#RequestParam("id") Integer groupId,#RequestParam("name") String groupName, Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
System.out.println("I am in the fetchCustomGroup controller");
int profileid=grpDao.getProfileId(name);
//model.addAttribute("customGroup",grpDao.fetchCustomGroup());
model.addAttribute("memberList",grpDao.fetchGroupMembers(groupId,profileid));
model.addAttribute("groupid",groupId);
model.addAttribute("profileid",profileid);
model.addAttribute("groupName",groupName);
System.out.println("groupid="+groupId);
System.out.println("groupName="+groupName);
return "manageGrps";
}
so instead of calling the fetchCustomGroup() in both the methods i just want to call in only one method and use the result in both the methods in the home controller.
so how can i use customGroup in another method to use the result of the fetchCustomGroup()
I think that what you want is to avoid executing the query twice. This can be done in different ways. The easiest way would be to assign the response to a variable in your controller and then use a getter instead of the dao. Controllers are singleton by default. Something like :
private Foo customGroup;
private synchronized Foo getCustomGroup() {
if(customGroup == null) {
customGroup = grpDao.fetchCustomGroup();
}
return customGroup;
}
And then use getCustomGroup() instead of grpDao.fetchCustomGroup()
I don't know what you are using for your persistence but using cache would also be a good idea to avoid executing the query twice.

Overloading a spring controller method with the same request mapping

I have a session attribute : user, and I have a url that I want to be viewed by both logged in users and publically by people not logged in as a user.
So what I want to do is this :
#Controller("myController")
#SessionAttributes({"user"})
public class MyController {
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id) {
return modelandview1;
}
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, #ModelAttribute User user){
return modelandview2;
}
However, I have a feeling its not going to work ... suggestions very welcome.
You only need the second method, the one that takes the User agument as well. When it's called without request attributes available to populate the User model, you'll just get a User instance with all null (or all default) field values, then in the body of the method you treat each situation accordingly
I don't think it's a right case for #SessionAttributes. This annotation is usually used to keep original instance of a form-backing object, to avoid passing irrelevant parts of its state via hidden form fields.
Your sceanrio is completely different, thus it would be better to use HttpSession explicitly:
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, HttpSession session) {
User user = (User) session.getAttribute(...);
if (user != null) {
...
} else {
...
}
}
Also note that #ModelAttribute is a subject to data binding - user can change its fields by passing request parameters. You definitely don't want it in this case.

Update method in Rest like controllers

I want to write rest like method for entity update. In this case I retrieve entity id from url and data from request body. The issue is in binding id with bean. Because neither EntityManager nor Spring-Data Crud Repo haven't update(id, bean) method. So I can set it myself
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String update(#PathVariable("id") Long id, #Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
user.setId(id); //Very bad
return "usersEdit";
}
user.setId(id); //Bad
repository.save(user);
return "redirect:/users/" + id;
}
or dismiss DRY and put id in forms as private field to.
Is there are any other solutions?
In Spring 3.1 a #ModelAttribute will be instantiated from a path variable if the path variable and the model attribute names are the same and there is a converter to instantiate the model attribute from the path variable value:
#RequestMapping(value="/{account}", method = RequestMethod.PUT)
public String update(#Valid #ModelAttribute Account account, BindingResult result) {
if (result.hasErrors()) {
return "accounts/edit";
}
this.accountManager.saveOrUpdate(account);
return "redirect:../accounts";
}
The full example is available at:
https://github.com/rstoyanchev/spring-mvc-31-demo

Categories

Resources