I'm in the process of writing a Spring REST type interface to a database that will retrieve user specific results for various resources.
To hold the user I have a spring #Component annotated bean called CurrentUser as a temporary measure.
#Component
public class CurrentUser {
#Autowired
private UserDAO userDAO;
private String userId;
private String email;
private String notes;
public String getUserId() {
return userId;
}
public void setUserId(String userId) throws ApiException {
User user = userDAO.getUser(userId) // Database call to
if (!user.isValid()) {
throw ApiException(...) // The exception would crash back to the user as a error in the response
}
// Valud user so set these aspects.
this.userId = user.userId;
this.email = user.email;
}
}
This object is initialised in a Spring Interceptor on each call to any method in the API using the following interceptor.
public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
#Autowired
private CurrentUser user;
#Autowired
private RequestParameters requestParameters;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ApiException {
user.setUserId(StringUtils.defaultString(request.getParameter("userId"), "defaultUser"));
return true;
}
}
This is only a place-holder to identify the users until proper authentication can be added.
I'm relatively new to Spring, and the reason for this post
is to increase my understanding of the thread safety aspects of Spring in situations like this
I've recently discovered that Spring is not automatically thread safe, I may need to give more consideration to the scopes.
What I want to understand is the following:
For the above setup, is there any danger that 1000s of simultaneous requests, would potentially interfere and overwrite each other?
e.g. A request for one user would potentially be overwritten with a different user from a separate http request, causing the requestor to receive the wrong data.
What would be the best approach to solving this. (Even though it will be replaced, I have other objects instantiated in similar ways)
Options I'm looking at (if this is an issue), is setting prototype scope, or attaching to the request / session directly rather than allowing them their own autowired object.
Any information anyone could give me would be much appreciated, as I'm a fan of getting it right(er) to start with, than dealing with bad assumptions later on.
Answer 1: Yes, and you don't need 1000 requests to get into trouble. 2 requests in parallel are enough.
Answer 2:
The main problem here is one of scoping:
Default scope of Spring managed beans is Singleton. That means that only one instance of your CurrentUser exists per Application.
That is obviously not what you want. (Since you have a severe security issue here, with only one CurrentUser instance per application).
Simple Answer:
I would probably use Spring Security ;-)
Even Simpler Answer:
If that is not an option:
Use a Filter instead of a HandlerInterceptor (more direct control over clean up)
Create a Thread Local to store the user (and use a finally in the Filter to clean it up) and set it in the Filter
Create a request scoped Service (use #ScopedProxy, to be able to wire it into Singletons), that accesses the ThreadLocal as a UserService (you will need an interface to make it work easily)
Autowire this UserService where you need it
Since by specification each request in a Servlet environment is bound to a thread, and thread locals are inherently thread-safe, you are completely thread safe and it will scale well. The overhead of the scoped proxy is minimal.
(This is only one option, other options could make use of the request scope explicitly or use aspects in a slightly more elegant manner. However, it is a rather simple approach and gets the job done. For more complex needs I would seriously recommend looking into Spring Security).
You ca use parameter resolver feature of spring mvc without making it a bean.
to do that implement the interface HandlerMethodArgumentResolver and register that into the container. And then your handler method can have a argument of type current user
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver{
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.getParameterType().equals(CurrentUser.class)) {
return true;
}
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
if (supportsParameter(parameter)) {
String userId = (String) webRequest.getAttribute("userId", NativeWebRequest.SCOPE_REQUEST);
return new CurrentUser(userId);
}
return null;
}
public class CurrentUser{
public CurrentUser(String userId) {
// TODO Auto-generated constructor stub
}
}
}
After this you can have handler method of stype
#RequestMapping
public String handler(CurrentUser user){
....
}
Related
I am implementing a mechanism for replacing short links.
I need to forwarded request to another controller. I found examples how to do it in spring on models, but I don't understand how to do it in RestControllers
Example what i found (use models)
#Controller
public class ShrotLinkForwardController {
#RequestMapping("/s/*")
public String myMethod(HttpServletRequest request) {
return "forward:/difmethod";
}
}
Or maybe I'm looking in the wrong direction and I need to make a filter?
UPD. I don't know the final endpoint, it is calculated in the forwarded method. So, i cant autowired other controller
There are 2 ways to achieve what you want.
1. Call the method on the target controller directly.
Controllers are just normal Spring beans. You can get it via autowire.
#Controller
public class ShrotLinkForwardController {
#Autowired
OtherController otherController;
#RequestMapping("/s/*")
public String myMethod(Model model) {
otherController.doStuff();
return ...;
}
}
2. Trigger the forward by returning a string
To trigger the forward, try returning a String instead of ModelAndView.
This is the approach you mentioned in your question. Note that the syntax should be forward:/forwardURL. The string after forward: is the URL pointing to another controller, not the method name.
#Controller
public class ShrotLinkForwardController {
#RequestMapping("/s/*")
public String myMethod(Model model) {
return "forward:/forwardURL";
}
}
you could inject the target controller and simply call the method
#Controller
public class ShortLinkForwardController {
#Autowired
private RestController target;
#RequestMapping("/s/*")
public String myMethod(HttpServletRequest request) {
return target.myMethod(request);
}
}
Caveat: Path related request properties will still point to "/s/*"
Or use ResponseEntity and set target location...
public ResponseEntity<Void> myMethod(HttpServletRequest request) {
return ResponseEntity.status(302).location(URI.create(...)).build();
}
All answers are about returning String
But I've found another solution
Maybe it will help someone with my problem in case when you need to make forward from one REST endpoint to another REST endpoint.
And it also could be applied to your case.
#RestController
public class CustomerController {
#GetMapping("/forwarding_endpoint")
public void makeForward(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getSession().getServletContext().getRequestDispatcher("/forward_endpoint").forward(request, response);
}
}
UPD. I don't know the final endpoint, it is calculated in the
forwarded method. So, i cant autowired other controller
but I don't understand how to do it in RestControllers
I can see some indications of possible bad design here, so I will try to explain the possible issues and how should be handled according to best practices.
If your requirement is to make a forward to another controller, then this might be an indication of 3 possible issues:
The job to be done by the other controller (which you say you want to forward to) can be extracted into a service method in service layer. Then both controllers can call the same service method, without each controller be aware of the other.
Your need could also be an indicator of the following issue. You need 2 controllers for exactly the same practical reason, so that they provide for same input exactly the same output, but to be available from 2 different URLs. If this is the case then you can use just 1 controller and allow it to be executed for both URLs. See the following code to achieve this:
#RequestMapping({"/s/*", "/s2/*})
public String myMethod(HttpServletRequest request) {
return "some response";
}
You need to expose only 1 URL to the client which will serve everything. Then the approach with forward will also not benefit you, since the client will be able to reach the other forwarded controller directly if he wishes so. In this case you can implement 1 single controller which then according to the needs builds different responses. You can do this in RestController although not suggested by Sonar and other code review tools by marking the method to return ResponseEntity<?>. Example:
#RequestMapping("/s/*")
public ResponseEntity<?> myMethod(HttpServletRequest request) {
if (condition 1) {
return new ResponseEntity<YourObject1>(HttpStatus.OK);
} else if (condition 2) {
return new ResponseEntity<YourObject2>(HttpStatus.OK);
} else {
return new ResponseEntity<YourObject3>(HttpStatus.OK);
}
}
this last choice is not considered best practice with <?> but for this requirement I don't see any other way out.
I am currently developing an API where I'm using DTO for the first time. So far I've used Spring's form validation with javax.validation.
So my question is if there is a way to combine both DTO and "form" validation. Let me explain myself: lets say I have a service to log in and another to register. In the service to register we have: name, password and email, the 3 of them must be filled. As for the login service, only the email and password must be filled. So we'd have something like:
private String name;
private String password;
private String email;
Until now, what I did was to create a POJO per request (forms) and then use annotations such as #NotNull but now with DTO in the project I'm in now they just have the same DTO and business object with the same properties and no constraints.
How could I do what I was usually doing? Checking the fields that must be not null in the controller looks a little dirty to me and I can't just put something like #NotNull in the UserDTO because then in the two examples I said I'd have to send also the name when logging in although it's not needed for that service.
So, how could I combine these 2 things? Is this something not possible or there's a better approach?
Thanks.
I assume you are using two separate controllers for login and register requests.
And if it is the case, then you can make good use of org.springframework.validation.Validator interface:
#Component("registrationValidator")
public class RegistrationValidatorImpl implements Validator {
#Override
public boolean supports(final Class<?> aClass) {
}
#Override
public void validate(final Object o, final Errors errors) {
}
}
Create RegistrationValidatorImpl and LoginValidatorIml and #Autowire it in your controllers.
The usage of validator is simple:
invokeValidator(registrationValidator, someDTO, errors);
if (errors.hasErrors()) {
return new ResponseEntity(HttpStatus.BAD_REQUEST); //or whatever logic here
}
The controller method signature should be similar to this:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity register(#RequestBody final SomeDTO someDTO, final HttpServletRequest request, final Errors errors) {}
I case of one controller, I assume you have different methods mapped to login and register requests. You can #Autowire both validators in controller and use each in separate methods.
Using groups for validation with javax.validation did the work. I followed the answer in this question (as Andrew suggested), then I just had to put every field I wanted to have different rules in different groups.
I have recently started looking into Spring Integration as a result of this question and have a question about object-type based routing.
The application I am maintaining needs to process requests from an incoming ActiveMQ queue (request.queue) and send a response back to the caller on a topic (response.topic). At present the requests are structured as follows:
public abstract class Request {
// base class
}
public abstract class CustomerRequest extends Request {
// base class for customer-specific requests
}
public class FindCustomerByIdRequest extends CustomerRequest {
private int id;
}
public class FindAllCustomersRequest extends CustomerRequest {
private boolean includeArchivedCustomers;
}
public class AddCustomerRequest extends CustomerRequest {
private String name;
private Date signupDate;
private Address address;
}
I have a service for each high level domain object which provides the functionality to service these incoming requests:
#Service
public class CustomerService {
public CustomerResponse findCustomerById(FindCustomerByIdRequest request) {
// code snipped
return customerResponse;
}
public AddCustomerResponse addCustomer(AddCustomerRequest request) {
// code snipped
return addCustomerResponse;
}
}
I need to route each specific request to the approriate method in CustomerService via #ServiceActivator which I understand can be done by creating a separate channel for each request and implementing a PayloadTypeRouter to place requests on the correct channel based on type.
Over time the list of request types is going to grow, and I am questioning whether a one-channel-per-request setup is efficient/practical/scalable. For example, if there are 100 different request types in the future there are going to be 100 different channels.
What would be great is if I could route the high-level requests of superclass CustomerRequest to CustomerService and have Spring work out the approriate method to call via an annotation or some other mechanism. Does anyone know if this is possible, or have any comments regarding the many-channels approach?
If there is no ambiguity, use <service-activator ... reg="fooBean" /> (no method) and the framework will chose the target method based on the payload.
If there is ambiguity (more than one method for the same type), it will fail.
However, a single class with 100+ methods is probably not a good design.
It seems to be that your request types are app feature specific. This suggest that you have one queue for all the possible application features. That is horrible idea. You should have at least separate queue per feature.
I suggest to rethink the design of your app.
I have a question about spring mvc and thread safety.
We are developing web application that will be stored on tomcat. If I understand right, Tomcat creates thread per request and it has some thread pool. Now, dispatcher servlet is shared between requests and probably is thread safe.
But when I create controller like this:
#Controller
#RequestMapping("/manage")
public class QuestionManagementController {
he has Singleton scope so the same controller is used by every request that comes from every user.
I am wondering how this problem is usually solved:
1: are controllers created with Session scope? (but I think that there also could be problems if one user quickly do some things that may lead to race conditions in the controller).
2: controllers are scoped as request
3: creating stateless controllers that don't share any variables at class level, or have them in read only mode
or maybe there is some better "best practice" that solves this kind of problem.
I am asking this question, because now we have them as Singleton scoped, and there is a problem, that in most methods we are querying for user in the database , and we can't store this information in class level variable because of the scope. We could try to use some thread safe collection but later there could be other resources needing synchronized access.
A lot of parameters can be added to the controller methods like request, session, principal etc to avoid your problem.
Usually there's a 3-layers architecture:
#Controller (they delegates to services)
#Service (they do the work using DAOs or Repositories)
#Repository (or DAOs, they do DB access)
So depending on what you are querying for in the DB, I would advise having a service, injected by Spring with a cache if hitting the DB is costly and everytime you need the stuff from the DB call the service (i.e. nothing stored in the controller class level)
A short example, let's say we are behind spring-security and everything need a fully logged-in user. We have an userData table where the key is the user login, we have an url /data to get a page showing my user data:
#Controller
#RequestMapping("/data")
public class UserDataController
{
#Autowired
private UserService userService;
#RequestMapping(value = "", method = RequestMethod.GET)
public ModelAndView data(final Principal principal) {
Assert.notNull(principal); // throw if assertion fails
Assert.hasText(principal.getName());
final UserData userData = this.userService.findByUserName(principal.getName());
Assert.notNull(userData, "userData not found");
return new ModelAndView("userData", "userData", userData);
}
}
#Service("userService")
public class userService
{
private static final String USERDATA_CACHE = "com.acme.foo.UserData";
#Autowired
private UserDataRepository userDataRepository;
#Cacheable(USERDATA_CACHE)
public UserData findByUserName(final String userName) {
return this.userDataRepository.findByUserName(userName);
}
}
#Repository
public class UserDataRepository
{
// or use spring-data-jpa
public UserData findByUserName(final String userName) {
// query table userData and create an UserData from results.
}
}
Here I use principal and spring ensure that this is current user one.
Refs:
#Cachable
see also Initialize Singletons in Spring Framework 3 MVC
Note sure if this answer fully to your concerns
Is there any way under spring 3.0 to access the HttpSession without including it in the method signature? What I really want to do is be able to pass in values from an HttpSession that CAN BE null.
Something like this:
#RequestMapping("/myHomePage")
public ModelAndView show(UserSecurityContext ctx) {}
instead of this:
#RequestMapping("/myHomePage")
public ModelAndView show(HttpSession session) {
UserSecurityContext ctx = (UserSecurityContext) session.getAttribute("userSecurityCtx");
}
The #SessionAttribute annotation mentioned by #uthark is not suitable for this task - I thought it was too, but a bit of reading shows otherwise:
Session attributes as indicated using
this annotation correspond to a
specific handler's model attributes,
getting transparently stored in a
conversational session. Those
attributes will be removed once the
handler indicates completion of its
conversational session. Therefore, use
this facility for such conversational
attributes which are supposed to be
stored in the session temporarily
during the course of a specific
handler's conversation.
For permanent session attributes, e.g.
a user authentication object, use the
traditional session.setAttribute
method instead. Alternatively,
consider using the attribute
management capabilities of the generic
WebRequest interface.
In other words, #SessionAttribute is for storing conversation MVC-model objects in the session (as opposed to storing them as request attributes). It's not intended for using with arbitrary session attributes. As you discovered, it only works if the session attribute is always there.
I'm not aware of any other alternative, I think you're stuck with HttpSession.getAttribute()
You can use a RequestContextHolder:
class SecurityContextHolder {
public static UserSecurityContext currentSecurityContext() {
return (UserSecurityContext)
RequestContextHolder.currentRequestAttributes()
.getAttribute("userSecurityCtx", RequestAttributes.SCOPE_SESSION));
}
}
...
#RequestMapping("/myHomePage")
public ModelAndView show() {
UserSecurityContext ctx = SecurityContextHolder.currentSecurityContext();
}
For cross-cutting concerns such as security this approach is better because you doesn't need to modify your controller signatures.
Yes, you can.
#SessionAttributes("userSecurityContext")
public class UserSecurityContext {
}
#RequestMapping("/myHomePage")
public String show(#ModelAttribute("userSecurityContext") UserSecurityContext u) {
// code goes here.
}
See for details:
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/SessionAttributes.html
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html