Is Request Scope thread safe in spring boot? - spring

I have created a parent bean that holds all the request scoped beans as shown below
#Data
#Component
#RequestScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
#NoArgsConstructor
public class AuthContext {
#Autowired
AuthContextManager authContextManager;
private AuthRequest authRequest;
private AuthResponse authResponse;
private AuthInfo authInfo;
public AuthContext(AuthRequest authRequest, AuthResponse authResponse, AuthInfo authInfo) {
this.authRequest = authRequest;
this.authResponse = authResponse;
this.authInfo = authInfo;
}
Here the authRequest, authResponse and authInfo are request scoped, As soon as I get a request, i modify the context as shown below and use this authcontext in all the spring components.
#PostMapping
public ResponseEntity<Object> sale(#RequestBody #Valid AuthRequest authRequest) throws Exception {
context.setContext(authRequest, new AuthResponse(), new AuthInfo());
Is this completely thread safe ?

Related

Why #Autowired and #Value fields are not injected in my class marked as #Component? [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 2 months ago.
I tried different solutions trying to use #Value within a class, even added #Autowire to the constructor, nut the #Value fields will still be null. I understand that this fields are injected after the construction of the object, but for me, their value is null, even if I just added a string, and not a property.
What am I doing wrong? I am using Spring boot 3, but anyway I have Controllers where this works, so probably I am wrong somewhere...
#Slf4j
#Component
public class TokenReceiver {
#Value("openid")
private String scope;
#Value("${spring.security.oauth2.client.registration.keycloak.client-id}")
private String clientId;
#Value("${spring.security.oauth2.client.registration.keycloak.client-secret}")
private String clientSecret;
private String grantType = "password";
#Autowired
private RestTemplate restTemplate;
public String getAccesToken(String username, String password) {
String accessTokenUrl = "https://keycloak.fh-kufstein.ac.at:8443/realms/BigOpenRealm/protocol/openid-connect/token";
LinkedMultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("scope", scope);
requestParams.add("grant_type", grantType);
requestParams.add("client_id", clientId);
requestParams.add("client_secret", clientSecret);
requestParams.add("username", username);
requestParams.add("password", password);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestParams, headers);
KeycloakToken keycloakAccessToken = getAccessTokenResponse(request, accessTokenUrl);
return keycloakAccessToken.getAccess_token();
}
and the class from which the method it is called:
#Slf4j
#Component
public class GetProxyStrategy extends AbstractProxyStrategy {
#Autowired
TokenReceiver tokenReceiver;
public GetProxyStrategy() {
super();
}
public GetProxyStrategy(HttpServletRequest httpServletRequest, HttpHeaders headers, RestTemplate restTemplate) {
super(httpServletRequest, headers);
}
private StatusAwareEntityHolder callWebservice(String serviceUrl,
String username, String password)
throws IOException, ProxiedWebServiceExecutionException {
String accessToken = tokenReceiver.getAccesToken(username, password);
both classes are in the packages that are scanned:
#SpringBootApplication(scanBasePackages = {"my.domain.boo.microservice.portal.*"})
Because you create the TokenReceiver object by yourself. In this class Spring has nothing to do - it doesn't interfere. So it doesn't inject anything and doesn't address your #Value or #Component annotation for this object.
Instead you should let spring create the instance for you.
Since you've put the #Component annotation, the chances are that it will be able to create a corresponding bean and put it onto the application context. So you should just inject it:
#Service // should be a bean by itself
public class MyWebServiceCaller {
#Autowired
TokenReceiver tokenReceiver; // <--- Note the injection here!
private StatusAwareEntityHolder callWebservice(String serviceUrl,
String username, String password)
throws IOException, ProxiedWebServiceExecutionException {
String accessToken = tokenReceiver.getAccesToken(username, password);
[...]
}
}

In MVC pattern, which is better to use HttpSession between Controller or Service?

Currently, by declaring HttpSession inside Service, session is created and the value for the key is retrieved.
#Service
#RequiredArgsConstructor
public class FormAnswerService {
private final FormAnswerRepository formAnswerRepository;
private final FormContentService formContentService;
private final MemberService memberService;
private final HttpServletRequest request;
public List<Long> createFormAnswer(Map<Long,String> answer) {
List<Long> formAnswerIdList = new ArrayList<>();
HttpSession httpSession = request.getSession();
Long memberId;
if(httpSession.getAttribute("login-user") != null) {
memberId = (Long)httpSession.getAttribute("login-user");
}
Is the above expression correct? Or is it correct to validate session in Controller in the first place?
In spring mvc session is usually used on controller. In spring you can use #SessionAttributes annotation to define session attributes on class scope and #ModelAttribute annotation in method scope.
#Controller
#SessionAttributes("login-user")
#RequestMapping("/formAnswer")
public class FormAnswerController {
#RequestMapping("/**")
public String handleFromAnswerRequest(#ModelAttribute("login-user") LoginUser loginUser,
Model model,
HttpServletRequest request) {
.......
}
}

#Value annotated field returning null method of #Service annotated class

I'm facing a strange issue while accessing a property using #Value annotation in a #Service annotated class with Spring Boot 2.2.1.RELEASE. The field is resolving to null and I'm not sure how to debug it.
#Service
public class MyService {
#Value("${my.username}")
private String username;
#Value("${my.password}")
private String password;
private RestTemplate restTemplate = getRestTemplate();
private RestTemplate getRestTemplate() {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
log.debug("Generating NTCredentials for RestTemplate using user: {} and password: {}", username, password);
credentialsProvider
.setCredentials(AuthScope.ANY,
new NTCredentials(username, password, null, "DOMAIN"));
HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build();
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setHttpClient(client);
return new RestTemplate(clientHttpRequestFactory);
}
}
In this code, inside the getRestTemplate() method the username and password are always coming as null.
I have confirmed that the properties are present in the application.properties file.
Any help would be really appreciated!
Edit 1:
#Slf4j
#Service
public class MyService {
#Value("${my.url}")
private String url;
private String username;
private String password;
private RestTemplate restTemplate = getRestTemplate();
public MyService(#Value("${my.username}") String username, #Value("${my.password}") String password) {
System.out.println(">> Inside Constructor");
this.username = username;
this.password = password;
}
public String updateDetails(String data) {
HttpEntity<String> entity = new HttpEntity<>(data);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
return response.getBody();
}
private RestTemplate getRestTemplate() {
System.out.println(">> Inside RestTemplate Method");
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
log.debug("Generating NTCredentials for RestTemplate using user: {} and password: {}", username, password);
credentialsProvider.setCredentials(AuthScope.ANY, new NTCredentials(username, password, null, "DOMAIN"));
HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build();
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setHttpClient(client);
return new RestTemplate(clientHttpRequestFactory);
}
}
Logs :
2019-11-25 19:56:47.069 INFO 20336 --- [ restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1293 ms
>> Inside RestTemplate Method
2019-11-25 19:56:47.361 DEBUG 20336 --- [ restartedMain] c.d.u.u.services.MyService : Generating NTCredentials for RestTemplate using user: null and password: null
>> Inside Constructor
[2
As you see, the getRestTemplate() is getting called before the constructor is getting called. Also, the user and password values are coming as null inside the getRestTemplate() method. However, if I do the initialization of the restTemplate inside the constructor then everything works fine.
this.restTemplate = getRestTemplate();
I thought the constructor was always called first. Could someone please explain this?
Use constructor injection
#Service
public class MyService {
private String username;
private String password;
#Autowired // optional if it is only single constructor
public MyService(#Value("${my.username}") String username, #Value("${my.password}") String password){
this.username=username;
this.password=password;
///its already here
}
This way you are guaranteed that values must be provided and available on object construction - thus it will be later on when you will call any of methods of that class.
If values are not provided OR SpEL is invalid, app will not boot.
You are calling getRestTemplate(); at the same time the class level field is being declared, which is before the class has been inflated by Spring framework, thus the properties aren't yet available.
Try setting the values in the class's constructor instead. You can do this by changing your variable declaration to : private RestTemplate restTemplate; and adding this constructor:
public MyService() {
this.restTemplate = getRestTemplate();
}
I think it has to do with the way you are accessing the restTemplate for instantiating the private variable. I do it this way.
#Autowire private RestTemplate restTemplate;
#Bean public RestTemplate restTemplate() {
...
}
This way Spring has control over when variables are instantiated.

Get return value in custom annotation spring aop

I have write a simple custom annotation to set HttpHeaders to ResponseEntity because of duplicating the code every where .
Annotation Interface and Class.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface JsonHeader {}
#Component
#Aspect
public class JsonHeaderAspect {
private final Log logger = LogFactory.getLog(getClass());
#Around(value = "#annotation(JsonHeader)")
public Object aroundServiceResponse(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
return proceedingJoinPoint.proceed(new Object[] {headers});
}
}
RestController Class
#RestController
#RequestMapping(path = "/login")
public class LoginRestController {
private final Log logger = LogFactory.getLog(getClass());
#Autowired
LoginServiceImpl loginService;
#JsonHeader
#RequestMapping(value = "/user",consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseBean> postCheckUser(#RequestBody LoginBean loginBean) {
ResponseBean responseBean = loginService.checkUser(loginBean);
return new ResponseEntity<ResponseBean>(responseBean,headers, HttpStatus.OK);
}
}
Now I want to get the return HttpHeaders value annotaion to rest controller class.
Is any one can describe why it happen and how to fix this issue it will be great helpful. Thanks in advance
You can do this simply by modifying your advice like this. You don't need to do anything in the controller.
#Around(value = "#annotation(requestMapping)")
public Object aroundServiceResponse(ProceedingJoinPoint proceedingJoinPoint,RequestMapping requestMapping) throws Throwable {
String[] consumes = requestMapping.consumes();
consumes[consumes.length] = MediaType.APPLICATION_JSON_VALUE;
String[] produces = requestMapping.produces();
produces[produces.length] = MediaType.APPLICATION_JSON_VALUE;
return proceedingJoinPoint.proceed();
}

Rest template giving null body and status 302

I am trying to consume a rest call in my mvc controller, however every time I do it returns a null body with http status as 302.Also I am using spring boot with spring security to get https.
I've followed code samples from here: http://websystique.com/springmvc/spring-mvc-4-restful-web-services-crud-example-resttemplate/
and Get list of JSON objects with Spring RestTemplate however none of these work
Can someone please point me in the right direction
Thank you,
REST
#RequestMapping(value = "/api/*")
#RestController
public class PostApiController {
static final Logger logger = LogManager.getLogger(PostApiController.class.getName());
private final PostService postService;
#Inject
public PostApiController(final PostService postService) {
this.postService = postService;
}
//-------------------Retrieve All Posts--------------------------------------------------------
#RequestMapping(value = "post", method = RequestMethod.GET)
public ResponseEntity<List<Post>> getAllPosts() {
List<Post> posts = postService.findAllPosts();
if(posts.isEmpty()){
return new ResponseEntity<List<Post>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<List<Post>>(posts, HttpStatus.OK);
}
}
Controller
#Controller
public class PostController {
static final Logger logger = LogManager.getLogger(PostController.class.getName());
public static final String REST_SERVICE_URI = "http://localhost:8080/api"; //"http://localhost:8080/api";
private final PostService postService;
#Inject
public PostController(final PostService postService) {
this.postService = postService;
}
#SuppressWarnings("unchecked")
#RequestMapping(value = "/getAll")
// public String create(#Valid Post post, BindingResult bindingResult, Model
// model) {
public ModelAndView getAll() {
// if (bindingResult.hasErrors()) {
// return "mvchome";
// }
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Post>> responseEntity = restTemplate.exchange(REST_SERVICE_URI+"/post",HttpMethod.GET, null, new ParameterizedTypeReference<List<Post>>() {});
// ResponseEntity<Post[]> responseEntity = restTemplate.getForEntity(REST_SERVICE_URI+"/post", Post[].class);
List<Post> postsMap = responseEntity.getBody();
MediaType contentType = responseEntity.getHeaders().getContentType();
HttpStatus statusCode = responseEntity.getStatusCode();
// List<LinkedHashMap<String, Object>> postsMap = restTemplate.getForObject(REST_SERVICE_URI+"/post", List.class);
// String s= REST_SERVICE_URI+"/post";
// logger.info(s);
if(postsMap!=null){
for(Post map : postsMap){
logger.info("User : id="+map.getUid());
}
}else{
logger.info("No user exist----------");
}
//List<Post> postList = postService.findAllPosts();
ModelAndView mav = new ModelAndView("mvchome");
mav.addObject("postsList", postsMap);
Post newpost = new Post();
mav.addObject("post", newpost);
return mav;
}
}
***** to fix my issue I modified my code to just do a redirect on select url paths instead of "/*"
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat =
new TomcatEmbeddedServletContainerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
//used to be just collection.addPattern("/*"); now I changed it to specify which path I want it to redirect
collection.addPattern("/mvchome/*");
collection.addPattern("/home/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(createHttpConnector());
return tomcat;
}
The http status 302 is usually caused by wrong url setting.
First, make sure that public ResponseEntity<List<Post>> getAllPosts() {} method is called (just print List<Post> result inside it).
If it's called properly and you can get the return value inside public ModelAndView getAll() {}.
The problem should be the directing setting of the public ModelAndView getAll() {} method.
Check if you make something wrong in your web.xml or spring configuration. Pay attention to the configuration which redirects to views and the url mapping of your dispatcher servlet.
If public ResponseEntity<List<Post>> getAllPosts() {} is called but you can't get the return value, then it should be the issues of directing setting of the public ResponseEntity<List<Post>> getAllPosts() {} method.
Check your spring configuration and web.xml for that. The possible cause usually will be the misuse of wildcard in the configuration and web.xml, or just unnoticed wrong mapping.

Resources