How to add object to session in spring app - spring

I have a kind of online store in Spring. I want to create a shopping cart for each new user who visits the site and stored it in the session. How can this be done? It's just that I've never worked with sessions in Spring. Perhaps there is a good resource to study this stuff.

#Controller
class example{
#RequestMapping("/")
public String test(HttpSession session){
session.addAttribute("cart",new Cart());
}
}

Make your controller session scoped
#Controller
#Scope("session")
then add one attribute to session
#RequestMapping(method = RequestMethod.GET)
public String testMestod(HttpServletRequest request){
ShoppingCart cart = (ShoppingCart)request.getSession().setAttribute("cart",valueOfCart);
return "testJsp";
}
then Scope the user object that should be in session every time:
#Component
#Scope("session")
public class User
{
String user;
/*getter setter*/
}
then inject class in each controller that you want
#Autowired
private User user
The AOP proxy injection : in spring -xml:
<bean id="user" class="com.User" scope="session">
<aop:scoped-proxy/>
</bean>
refer:How to use Session attributes in Spring-mvc

Related

How to pass Injected/Autowired object from Spring to ManagedBean?

I am working on a project with Spring and EJB/Primefaces and I want to pass values from the spring context to a managed bean. I will demonstrate with a sample code to clarify further.
Let's say we have the following domain class (I keep it simple for better readability):
public class Store {
#JsonProperty("store_name")
private String storeName;
//constructors, getters and setters...
}
The reason of #JsonProperty is because I am getting this value from an other application that POSTs a Json to the following Controller:
#Controller
#RequestMapping("/store")
public class StoreController {
#Autowired
private Store store;
#RequestMapping(method = RequestMethod.POST)
public String getStoreResponse(#RequestBody String store) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
LOGGER.info("Store Before Post: " + store.getName());
store = mapper.readValue(request, Store.class);
LOGGER.info("Store After Post: " + store.getName());
return "store";
}
}
I have configured the store bean at a BeanConfig class:
#Configuration
public class BeanConfig {
#Bean(name = "store")
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Store store() {
Store store = new Store();
store.setName("Test Store Name");
return store;
}
}
This is my managed bean:
#ManagedBean
#SessionScoped
public class StoreView extends SpringBeanAutowiringSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(Store.class);
//#ManagedProperty("#{store}")
#Autowired
private Store store;
public void test() {
LOGGER.info("TEST " + store.getName());
}
//getters and setters
}
finally my xhtml:
<h:panelGrid columns="3">
<p:outputLabel for="j_store" value="#{messages['storeview.name']}" />
<p:inputText id="j_store" value="#{storeView.store.name}" />
<p:message for="j_store" />
<h:panelGroup />
<p:commandButton value="#{messages['storeview.test']}" action="#{storeView.test}" update="#form" ajax="false" />
</h:panelGrid>
When I am posting sample data using postman, the first time, the logger outputs:
10:35:57,433 INFO [com.store.test.controllers.StoreController] (default task-2) Store Before Post: Test Store Name
10:35:57,488 INFO [com.store.test.controllers.StoreController] (default task-2) Store After Post: posted store name
and if I continue calling the controller I keep getting the "posted store name", so it has kept the value.
But when I am going to the store.xhtml and hit the test button to submit the form, it still has the value set in the bean configuration file ("Test Store Name") and from that point on it keeps the value that I submit in the inputText.
I suspect it has to do with Spring and Faces context, I do not know if what I want to do is possible. If it is, please point out what should I change to make it work, otherwise, please provide me with an alternative solution.
Thanks in advance.
You are mixing #Autowired and #ManagedBean annotations.
#Autowired is managed by Spring while #ManagedBean is managed by JSF.
That means that probably you will have 2 instances of Store, the one modified by controller is not the same instance used by the managed bean.
You should annotate as #ManagedProperty("#{store}") your store attribute in managed bean and define getter and setter.
To get it to work you also must define spring Expression Language resolver in faces-config.xml
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
Since jsf session is different from mvc session, you also have to use singleton scope in the definition of Store object.
#Scope(value = "singleton"........

Spring mvc - #sessionattributes vs #Scope("session") beans which to use?

I'm not fully understanding when to use #SessionAttributes vs #Scope("session") beans.
Currently, I'm doing the following
#ControllerAdvice(assignableTypes = {DashboardController.class, FindingWholeSalersController.class})
public class AuthControllerAdvice {
private IFindWholeSalerService service;
public IFindWholeSalerService getService() {
return service;
}
#Autowired
public void setService(IFindWholeSalerService service) {
this.service = service;
}
//put firstname in session etc..
#ModelAttribute
public void addWholesalerDiscoveryCountToSession(Model model, Principal principal){
if (!model.containsAttribute("firstname")) {
String firstname = service
.findUserFirstName(principal.getName());
model.addAttribute("firstname",
firstname);
}
}
Notice this if test if (!model.containsAttribute("firstname"))
Basically, if the session attribute is already in the model, then I dont want to ask my service layer to make a database request. However, every #RequestMapping call in any of the controllers I'm advising, first makes a call to
#ModelAttribute
public void addWholesalerDiscoveryCountToSession(Model model, Principal principal)
Does the if test, and moves on its marry way.
Is this the right solution for keeping data in the session so you dont have to call your database, OR would #Scope("session") beans be a better choice OR something else?
Thanks for all advice in advance!

Not understanding Spring SessionAttribute and Autowiring

I'm not a Spring expert and I'm facing a behavior I don't understand...
I have a SessionAttribute "user" in my Controller, that is autowired to my bean User.
When I log in, my User is populated with some values etc.
When I log out, I am expecting that my session attribute "user" would be reset, but it keeps its values.
So where is the problem? Is my log out not working properly? Or is it normal and so, could someone explain me what is happening inside Spring please?
Here is a code Sample to understand my question:
#Controller
#SessionAttributes("user")
public class HomeController
{
#Autowired
private User user;
// Session Attribute
#ModelAttribute("user")
public User setSessionAttribute()
{
LOGGER.debug("Adding user to session...");
return user;
}
...
}
Edit: logout sample code and user declaration
My User is declared like this:
#Component
public class User
{
...
}
To log out I have a link pointing to /myapp/j_spring_security_logout and I have implemented a logout handler:
#Component
public class MyLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler
{
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException
{
//to check if user is in session, but it's not
Enumeration<String> e = request.getSession().getAttributeNames();
//some code needed to log out from my custom security manager
//kill the session (not spring session) and redirect to the specified url
agent.logout("/myapp/login");
super.onLogoutSuccess(request, response, authentication);
}
}
Now that you've posted User
#Component
public class User
{
...
}
you will notice that it is has Singleton scope. The bean autowired here
#Autowired
private User user;
is that singleton instance. It will always be the same regardless of what Session or request you're processing and regardless of you logging out. So up to now, all your users have been sharing the same User instance.
You can change it to have Session scope.
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
#Component
public class User
{
...
}
Now each Session will have its own instance to work with.
I believe SimpleUrlLogoutSuccessHandler is not clearing the content of the session.
SimpleUrlLogoutSuccessHandler only invokes the handle() method in AbstractAuthenticationTargetUrlRequestHandler and it's Javadoc says:
Invokes the configured RedirectStrategy with the URL returned by the determineTargetUrl method.
The redirect will not be performed if the response has already been committed.
Simplest solution would be to remove this attribute from the session by:
request.getSession().removeAttribute("user");

Spring dependency injection depending on request object

I am working on a web application with spring. The application is configured with a properties file.
There are multiple instances of the application in different servers, each instance has a different configuration file (each instance is customized for a different customer) I am using controllers and services. Something like this:
public class Controller1 {
#Autowired
Service1 service1;
#RequestMapping(value = "/page.htm", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView serve(HttpServletRequest request, HttpServletResponse response) {
service1.doSomething();
return new ModelAndView("/something");
}
}
#Service
public class Service1 {
#Autowired
Service2 service2;
public void doSomething () {
…
service2.doAnotherThing();
…
}
}
#Service
public class Service2 {
#Value("${propertyValue}")
private String propertyValue;
//doAnotherThing() will use propertyValue
public void doAnotherThing () {
…
//Do something with propertyValue
…
}
}
Now I have a new requirement. There won’t be multiple instances for each customer, but only one instance with multiple domains for all the customers.
The application must decide the configuration depending on the host name of the request object in the controller. So if the customer points the browser to www.app1.com I have to use configuration file 1 but if the customer uses www.app2.com I have to use configuration 2 and so on.
I moved the configuration files to the database, but then I realized that I do not know how to make the dependency injection. The services are linked, service1 uses service2 and service2 is the one who must use the value which depends on the configuration. Service 2 has no knowledge of the request object.
Is there a clean way to solve this?
Thanks,
One approach is to create configuration object for all customer as a singleton on spring config:
<bean id="customerAConfig"../>
<bean id="customerBConfig"../>
<bean id="customerCConfig"../>
And have a session scoped ConfigurationService that acts as a pointer to which configuartion is active
public class ConfigurationService {
private CustomerConfig activeConfig;
// getters & setters..
}
Configure a singleton proxy for this service on your spring config so it can be injected into singleton components. You need to have cglib in your classpath for Spring to create the proxy:
<bean class="com.mycompany.ConfigurationService" scope="session">
<aop:scoped-proxy/>
</bean>
And on your login controller, select which configuration should be used by virtual host name and store it into ConfigurationService for later retrieval (remember ConfigurationService is session scoped)
public class LoginController {
#Autowired private CustomerConfig[] custConfigs;
#Autowired private ConfigurationService configService;
#RequestMapping(method = POST)
public String login(HttpServletRequest request, ..) {
...
String host = request.getServerName();
CustomerConfig activeConfig = // decide which one based on host..
configService.setActiveConfig(activeConfig);
...
}
}
Below is a sample FooController that reads customer specific configuration
#Controller
#RequestMapping("/foo")
public class FooController {
#Autowired private ConfigurationService configService;
#RequestMapping(method = "GET")
public String get() {
...
CustomerConfig config = configService.getActiveConfig();
...
}
...
}
If your program does not have a single entry point like a login page, you can code similar logic as a filter. Check if an active configuration is set on session, if not look it up based on host name

Accessing a session-scoped bean inside a controller

I'm experimenting with session-scoped beans in Spring 3. I have the following bean definition:
<bean id="userInfo" class="net.sandbox.sessionbeans.UserInfo" scope="session" />
Here is net.sandbox.controllers.RegistrationController, a controller class that needs access to this bean. I've taken out the imports for brevity's sake.
#Controller
#RequestMapping("/register")
public class RegistrationController {
private UserInfo userInfo; // This should reference the session-scoped bean
#RequestMapping(method = RequestMethod.GET)
public String showRegForm(Model model) {
RegistrationForm regForm = new RegistrationForm();
model.addAttribute("regform", regForm);
return "regform";
}
#RequestMapping(method = RequestMethod.POST)
public String validateForm(#Valid RegistrationForm regForm, BindingResult result, Model model) {
if (result.hasErrors()) {
return "regform";
}
userInfo.setUserName(regForm.getFirstName());
model.addAttribute("regform", regForm);
return "regsuccess";
}
}
Is there a way to automatically tie the session-scoped bean I defined to the member variable private UserInfo userInfo in RegistrationController?
Yes - see section 3.4.5.4 of the Spring manual, "Scoped beans as dependencies".
Briefly, you can ask Spring to wrap your session-scoped bean in a singleton proxy, which looks up the correct session when you invoke a method on the scoped bean. This is called a "scoped proxy", and uses the <aop:scoped-proxy> config macro. You can then inject the reference as you would any other (e.g. <property>, or #Autowired). See the above link for details.
By default, Spring creates a proxy by implementing an interface at run-time. So, the only methods available on the proxy are those defined in any interfaces UserInfo implements (if any). You may have to create a suitable interface that includes the setUserName() method.
Alternatively, you will need to force a CGI based proxy (the proxy is a sub-class of your class created at run-time so it doesn't need an interface). Specify:
<bean id="userInfo" class="net.sandbox.sessionbeans.UserInfo" scope="session" >
<aop:scoped-proxy proxy-target-class="true"/>
</bean>
About this comment:
I tried applying this technique. I put
inside the bean
definition and I #Autowired'd private
UserInfo userInfo. It seems to work,
but for some reason the bean's setter
function isn't executed properly...
i.imgur.com/zkxVA.png – Pieter 1 hour
ago
If you use interface-based proxies, the setter method is not available on the Proxy unless the interface has the setter method.
If you don't like XML, you can also use ObjectFactory<T> like this :
#RestController
public class MyController {
private final ObjectFactory<MySessionScopedComponent> OFSession;
#Autowired
public MyController(ObjectFactory<MySessionScopedComponent> OFSession) {
this.OFSession = OFSession;
}
#RequestMapping(path = "/path", method = RequestMethod.GET)
public String myMethod () {
MySessionScopedComponent sessionBean = OFSession.getObject();
// Do some stuff
return bean.myValue();
}
}
Note: Tested with Spring Boot 1.5.6 (Spring 4.3)

Resources