#Value annotated field returning null method of #Service annotated class - spring-boot

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.

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);
[...]
}
}

Is Request Scope thread safe in spring boot?

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 ?

unable to mock RestTemplate response

I wanted to mock the RestTemplate's result, but with my code below, it always went to do the Http. Can someone please advise? I think I have parts that I did wrongly, please help.
UserServiceTest.java
#RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceTest(){
#InjectMock
#Autowired
UserService userservice;
#Mock
RestTemplate restTemplate;
#Value(${aa.bb.cc})
private String getfromapplicationpropertiesVal;
#Test
public void test1(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String jsonBody = null;
HttpEntity<String> entity = new HttpEntity<>(jsonBody, headers);
String textContent = "result from junit";
ResponseEntity<String> response = new ResponseEntity<>(textContent, HttpStatus.OK);
String url = "http://localhost:8080/test/test.txt";
doReturn(response).when(restTemplate).exchange(
eq(url),
any(HttpMethod.class),
any(HttpEntity.class),
any(Class.class)
);
userservice.test();
}
}
UserService.java
#Service
public class UserService{
#Autowired
HttpHelperService httpHelperService;
public void test(){
String url = "http://localhost:8080/test/test.txt";
String response = httpHelperService.cal(url, HttpMethod.GET);
System.out.println(response);
}
}
HttpHelperService.java
#Service
public class HttpHelperService{
#Autowired
private RestTemplate restTemplate;
public String cal(String url, HttpMethod httpMethod){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String jsonBody = null;
HttpEntity<String> entity = new HttpEntity<>(jsonBody, headers);
String response = restTemplate.exchange(url, httpMethod, entity, String.class); //This part kept calling http when run the #Test
}
}
RestTemplateConfig
#Configuration
public class RestTemplateConfig{
#Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
Recommendation in these type of scenario is to use MockRestServiceServer & not mocking restTemplate.
You might do something like this:
#Mock
RestTemplate restTemplate;
#Autowired
MockRestServiceServer mockServer;
#BeforeEach
public void init(){
mockServer = MockRestServiceServer.createServer(restTemplate); //initialization
}
And then invoke mock for your service something like :
mockServer.expect(requestTo(url))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("successOperation"));
-Alternatively, you can just mock your test() method:
when(mockedHttpHelperService).cal(anyString(),HttpMethod.GET).thenReturn("success");

How to mock resttemplate calls in spring boot?

I tried to write test cases for the rest calls in my service which is calling the 3rd party api.
#RunWith(MockitoJUnitRunner.class)
public class ForceServiceTest {
private ForceService forceService;
#Mock
private ForceServiceConfig config;
#Mock
private RestTemplate restTemplate;
#Before
public void setup() {
forceService = new ForceService(config);
}
#Test
public void apiCall_valid() throws JSONException {
HttpHeaders headers = new HttpHeaders();
headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
headers.set(ACCEPT, APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(
"id=null",
headers);
config.authTokenUrl = "https://ex...com/..";
Mockito.when(restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity, Access.class)).thenReturn(null);
// Mockito.when(any()).thenReturn(null);
forceService.apiCall();
}
}
#Component
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate = new RestTemplate();
public ForceService(ForceServiceConfig config) {
this.config = config;
}
private String apiCall() {
HttpHeaders headers = new HttpHeaders();
headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
headers.set(ACCEPT, APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(
"&id=" + config.id,
headers);
ResponseEntity<Access> response = restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity,
Access.class);
return response.getBody().token_type + " " + response.getBody().access_token;
}
}
I get the following error:
org.springframework.web.client.HttpClientErrorException: 404 Not Found
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
It is calling the api in test class, which I do not want to happen.
I need to mock the resttemplate call of 3rd party api. How can I do it without being actually calling the api?
This is the problem
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate = new RestTemplate(); // HERE
you are creating new REAL rest template. What you probably want is to
Use mock that you created in wrapping test class
Use real rest template and SPY on it.
A don't know is that your actualy structure (1 file 2 classes) but it is safe to assume it is not and in any case you can simply pass RestTemplate as ctor argument. So
#Component
public class ForceService {
private ForceServiceConfig config;
private RestTemplate restTemplate;
public ForceService(ForceServiceConfig config,RestTemplate restTemplate) {
this.restTemplate=restTemplate;
this.config = config;
}
and
#Before
public void setup() {
forceService = new ForceService(config,restTemplate);
}
Now if you want to RestTemplate to be just a stub that does literally nothing and return null on any calls if not instructed otherwiser - leave it as #Mock.
If you want however, to allow it to work normally and only intercept some specific method calls and stub responses, use spy.
#Mock
private RestTemplate restTemplate;
private RestTemplate restTemplate=Mockito.mock(RestTemplate.class)
or
private RestTemplate restTemplate=Mockito.spy(new RestTemplate());

How to test spring rest operation exchange

I have a problem with test of my method which use RestOperation exchange method. When i'm trying to mock response i get an error:
ResponseEntity cannot be returned by toString()
toString() should return String
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Below is my class, which i want to test
#Component
public class AuthGateway {
#Autowired
AuthorizedHttpEntityFactory authorizedHttpEntityFactory;
#Autowired
RestOperations restOperations;
#Value("${authServer.host}:${authServer.port}/${authServer.validateToken.path}")
private String authPath;
#Value("${authServer.host}:${authServer.port}/basic/check")
private String basicAuthPath;
#Value("${authServer.tokenName}")
private String tokenName;
#Value("${authServer.host}:${authServer.port}/user")
private String userProfileUrl;
#Value("${authServer.host}:${authServer.port}/homeowner")
private String homeownerUrl;
public UnpackedToken authenticate(String token) throws ResourceAccessException, AuthException {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add(tokenName, token);
HttpEntity httpEntity = authorizedHttpEntityFactory.getAuthorizedHttpEntity(formData);
Map map = null;
try {
ResponseEntity<Map> entity = restOperations.exchange(authPath, HttpMethod.POST,
httpEntity, Map.class);
map = entity.getBody();
} catch (RestClientException e) {
processError(e);
}
#SuppressWarnings("unchecked")
Map<String, Object> result = map;
return new UnpackedToken(result);
}
and Test class
#RunWith(MockitoJUnitRunner.class)
public class AuthGatewayTest {
private ResponseEntity<Map> entity;
#Mock
private RestOperations restOperations;
#Mock
private LinkedMultiValueMap linkedMultiValueMap;
#Mock
private AuthorizedHttpEntityFactory authorizedHttpEntityFactory;
#Autowired
#InjectMocks
private AuthGateway authGateway;
private String token;
private Integer userId = 1;
private String role = "ROLE_PRO";
private UnpackedToken unpackedToken;
private Map<String, Object> map;
private RestClientException restClientException;
private AuthException authException;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
restClientException = new RestClientException("Test exception");
authException = new AuthException("Test exception");
token = "token-token";
map = new HashMap<>();
map.put("UserId", userId);
map.put("authorities", Collections.singletonList(role));
entity = new ResponseEntity<>(map, HttpStatus.OK);
unpackedToken = new UnpackedToken(map);
}
#Test
public void testAuthenticateSuccessfully() throws Exception {
HttpEntity httpEntity = new HttpEntity("body");
Mockito.when(authorizedHttpEntityFactory.getAuthorizedHttpEntity(any(Map.class))).thenReturn(httpEntity);
Mockito.when(restOperations.exchange(
Mockito.anyString(), Mockito.<HttpMethod>any(), Mockito.<HttpEntity<?>>any(), Mockito.<Class<Map>>any())).
thenReturn(entity);
Mockito.doNothing().when(linkedMultiValueMap).add(any(), any());
assertEquals(this.unpackedToken, authGateway.authenticate(token));
}
What is wrong with this mock?
Weird, when i change mock line into:
Mockito.when(restOperations.exchange(
Mockito.anyString(), Mockito.<HttpMethod>any(), Mockito.<HttpEntity<?>>any(), Mockito.<Class<Map>>any())).
thenReturn(new ResponseEntity<>(map, HttpStatus.OK));
then it starts working properly...

Resources