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.
Related
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)
I want to make web application with REST and spring boot. Rest web service is stateless. I want to make it stateful so that the information server send to client after first request should be used in upcoming request. Or the execution done in first or second request will be used further.
Can we generate some session id for this and this session id client can send to sever in followed requests ? If yes, then
if state is changing (values get modified due to some manipulation) of some objects/beans. So how can we save the state inorder of objects/beans to make it stateful and what is the scope of these beans (whose value will be modified) and those classes/beans which will give call to these beans as multiple clients or users will be using this web application ?
Restful API's are not stateful by design, if you make them stateful using a server side then its not REST!
What you need a correlation Id which is a recognised pattern in a distributed system design. Correlation Id will let you tie requests together.
Sessions are typically an optimization to improve performance when running multiple servers. They improve performance by ensuring that a clients requests always get sent to the same server which has cached the clients data.
If you only want to run a single server, you won't have to worry about sessions. There are two common approaches to solve this problem.
1. In Memory State
If the state you want to maintain is small enough to fit into memory, and you don't mind losing it in the event of a server crash or reboot, you can save it in memory. You can create a spring service which holds a data structure. Then you can inject that service into your controllers and change the state in your http handlers.
Services are singletons by default. So state stored in a service is accessible to all controllers, components, and user requests. A small pseudo example is bellow.
Service Class
#Service
public class MyState
{
private Map<String, Integer> sums = new HashMap<>();
public synchronized int get(String key) {
return sums.get(key);
}
public synchronized void add(String key, int val) {
int sum = 0;
if (sums.contains(key)) {
sum = sum.get(key);
}
sum += val;
sums.put(key, (Integer)sum);
}
}
Controller Class
#RestController
#RequestMapping("/sum")
public class FactoryController
{
#Autowired
private MyState myState;
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public SuccessResponse saveFactory(#RequestBody KeyVal keyVal)
{
myState.add(keyVal.getKey(), keyVal.getValue());
}
}
Note: This approach will not work if you are running multiple servers behind a load balancer, unless you use a more complex solution like a distributed cache. Sessions can be used to optimize performance in this case.
2. Database
The other option is to just use a database to store your state. This way you won't lose data if you crash or reboot. Spring supports the hibernate persistence framework and you can run a database like Postgres.
Note: If you are running multiple servers you will need a more complex solution since hibernate caches data in memory. You will have to plug hibernate into a distributed cache to synchronize in memory state across multiple servers. Sessions could be used as a performance optimization here.
Important
Whenever you are modifying state you need to make sure you are doing it in a thread safe manner, otherwise your state may be incorrect.
What is the best pattern to follow when saving an object that belongs to a authenticated user using Spring Security and Web MVC?
All the tutorials that I've come across show how to authenticate a user logging in and that's as far as they go, they don't explain how to work with multiple users and their data. For example: A logged in user is saving a new blog post. Here is what the controller looks like:
#PostMapping("/blog/save")
public String blogSavePost(#AuthenticationPrincipal CurrentUser currentUser, BlogForm blogForm) {
blogService.save(currentUser, blogForm);
return "redirect:/blog";
}
Should the currentUser be passed to the blogService where the service then sets it on the entity Blog (which is then passed to BlogRepository for saving)? It seems a bit tedious to have to pass this on every save or edit..
What about when editing an object - since the id of the object being saved is passed to the controller (either as a parameter of part of the form object), it's open to being changed by a user. How should the service layer verify that the data being saved belongs to the user? I figure that there is an easy way to handle this with Spring Security, I just haven't come across what it is..
Question 1
Entirely up to you. I would just set the association in the Controller before passing it to the service:
Question 2.
You can prevent binding of certain fields by defining a binder in youir controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields("userId");
}
You can also use method level security to prevent users from editing entities other than those that belong to them. See here for an example:
Spring Security in conjuction with Spring repositories to secure and authenticate various parts of a website
Alternatively you could use a Web Security expression that would secure the endpoint for save/update/delete requests.
http://docs.spring.io/spring-security/site/docs/current/reference/html/el-access.html#el-access-web
I read a few tutorials about forms and submission in Spring 3 MVC. All these examples indicate that storing the backing object in the session the following way:
#SessionAttributes({"command"})
I used old versions of Spring in which there is a formBacking method if a controller inherits SimpleFormController.
protected Object formBackingObject(HttpServletRequest request)
If my understanding is right, the old version approach loads the form backing object on the fly when the form submits and there is no need of storing this object in session.
If my understanding about Spring 3 MVC is right, then I don't like the session approach because it consumes lot of memory in case of large users and the object stored in session might be outdated at the time of form submission.
Can I avoid storing the form backing object in session in Spring 3's MVC? Any pointer?
Thanks for any input and please correct me if I am wrong.
Regards.
Anyone else knows whether my understanding is right?
the GET controller adding the object to the model equates to adding it to session. How can Spring remember the object as the backing object when a form is submitted? Cheers.
How can the form backing object added to the model in the GET controller survive a round trip between the server and client when a form is submitted? Does Spring serialize it on disk? I am guessing...
Thanks for any input!
If you simply do not list your command among the #SessionAttributes, it will be instantiated every time.
In the old Controller API, implementations could decide whether or not they want to keep their command objects on the session: see AbstractFormController.isSessionForm(). SimpleFormController returned false, while AbstractWizardFormController returned true, as it actually required the command to be stored on the session.
You can check how a model attribute is bound in the HandlerMethodInvoker.resolveModelAttribute(...) private method:
if (implicitModel.containsKey(name)) {
// ...
} else if (this.methodResolver.isSessionAttribute(name, paramType)) {
// ...
else {
bindObject = BeanUtils.instantiateClass(paramType);
}
So obviously, the binder will use a fresh instance if you don't explicitely declare it as session attribute; exactly the same as returning false from AbstractFormController.isSessionForm().
How can the form backing object added to the model in the GET controller survive a round trip between the server and client when a form is submitted? Does Spring serialize it on disk? I am guessing...
Spring does not (always) need to store the backing object between form view and form submission. If everything that you need in order to populate your command object comes with the request, Spring can simply instantiate a new command object (either itself or let you do it if you provide a proper method) and then bind the request to that fresh instance.
See above the comparison with the old API regarding the difference between SimpleFormController and AbstractWizardFormController implementations. The first does not need to store anything on the session and it will bind the submit request to a newly created object. With the new annotated handlers, the flow is the same; if you don't want to store it, it will be recreated on submit: BeanUtils.instantiateClass(...) or your own custom factory method.
You need to add the backing object to the model, not to the Session. Here's one tutorial... http://www.roseindia.net/tutorial/spring/spring3/web/spring-3-mvc-form-example.html
I need to write a small Java class that will enable me to add to and read from the current user session.
Everything I see refers to Servlets but I'd ideally like to just use a plain old class.
Can anyone please help this Java Newbie?
Thanks
The general concept of a "Session" is really just a data storage for an interaction between a HTTP client and server. Session management is automatically handled by all HTTP Servlets. What framework?
If you're just wanting to store information for a console app to remember information between runs, then consider using Xml to save/load data from a file.
Use a component based MVC framework which abstracts all the ugly Servlet API details away so that you ends up with zero javax.servlet imports. Examples of such are JSF2 and Struts2.
In JSF2 for example, you'd just declare User class as a session scoped managed bean:
#ManagedBean
#SessionScoped
public class User {
// ...
}
Then in the "action" bean which you're using to processing the form submit, reference it as managed property:
#ManagedBean
#RequestScoped
public class SomeFormBean {
#ManagedProperty(value="#{user}")
private User user;
public void submit() {
SomeData someData = user.getSomeData();
// ...
}
}
That's it.
If you'd like to stick to raw Servlet API, then you've to live with the fact that you have to pass the raw HttpServletRequest/HttpServletResponse objects around. Best what you can do is to homegrow some abstraction around it (but then you end up like what JSF2/Struts2 are already doing, so why would you homegrow -unless for hobby/self-learning purposes :) ).
Yes, just pass the HttpRequest to your class from your servlet.
In your servlet do something like this,
cmd.execute(request);
In your class do something like this,
public class Command implements ICommand {
.
.
public void execute(HttpServletRequest request){
HttpSession sess = request.getSession(false);
}
.
.
.
}
In general, as mentioned in the other answers, session in many ways acts as a store. So to interact wth a session from another class which is outside of the Servlet/JSP framework the reference to the session in question needs to be procured. There are few ways it can be achieved:
1) Passing the session as part of a method parameter (already mentioned in other answers)
2) Binding the session to a thread local variable on to the current thread executing (refer ThreadLocal). This method has the advantage of not declaring specific parameters on the method signature of the class that needs to use the session. In addition, if the calling thread goes through a library and then again calls some specific class e.g. Servlet->YourClass0 -> Apache Some Library -> YourClass1, the session will also be available to YourClass1.
However, the thread local also needs to be cleared when the executing thread returns through the initial component (servlet let's say) otherwise there certainly could be memory leaks.
In addition, please refer to your specific framework for treatement of sessions, the above mechanism works fine in Tomcat.