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

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

Related

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 ?

#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.

Using #Headers with dynamic values in Feign client + Spring Cloud (Brixton RC2)

Is it possible to set dynamic values to a header ?
#FeignClient(name="Simple-Gateway")
interface GatewayClient {
#Headers("X-Auth-Token: {token}")
#RequestMapping(method = RequestMethod.GET, value = "/gateway/test")
String getSessionId(#Param("token") String token);
}
Registering an implementation of RequestInterceptor adds the header but there is no way of setting the header value dynamically
#Bean
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Auth-Token", "some_token");
}
};
}
I found the following issue on github and one of the commenters (lpborges) was trying to do something similar using headers in #RequestMapping annotation.
https://github.com/spring-cloud/spring-cloud-netflix/issues/288
Kind Regards
The solution is to use #RequestHeader annotation instead of feign specific annotations
#FeignClient(name="Simple-Gateway")
interface GatewayClient {
#RequestMapping(method = RequestMethod.GET, value = "/gateway/test")
String getSessionId(#RequestHeader("X-Auth-Token") String token);
}
The #RequestHeader did not work for me. What did work was:
#Headers("X-Auth-Token: {access_token}")
#RequestLine("GET /orders/{id}")
Order get(#Param("id") String id, #Param("access_token") String accessToken);
#HeaderMap,#Header and #Param didn't worked for me, below is the solution to use #RequestHeader when there are multiple header parameters to pass using FeignClient
#PostMapping("/api/channelUpdate")
EmployeeDTO updateRecord(
#RequestHeader Map<String, String> headerMap,
#RequestBody RequestDTO request);
code to call the proxy is as below:
Map<String, String> headers = new HashMap<>();
headers.put("channelID", "NET");
headers.put("msgUID", "1234567889");
ResponseDTO response = proxy.updateRecord(headers,requestDTO.getTxnRequest());
I have this example, and I use #HeaderParam instead #RequestHeader :
import rx.Single;
import javax.ws.rs.Consumes;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
#Consumes(MediaType.APPLICATION_JSON)
public interface FeignRepository {
#POST
#Path("/Vehicles")
Single<CarAddResponse> add(#HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader, VehicleDto vehicleDto);
}
You can use HttpHeaders.
#PostMapping(path = "${path}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<?> callService(#RequestHeader HttpHeaders headers, #RequestBody Object object);
private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "1234");
headers.add("CLIENT_IT", "dummy");
return headers;
}
I use #HeaderMap as it seems very handy if you are working with Open feign. Using this way you can pass header keys and values dynamically.
#Headers({"Content-Type: application/json"})
public interface NotificationClient {
#RequestLine("POST")
String notify(URI uri, #HeaderMap Map<String, Object> headers, NotificationBody body);
}
Now create feign REST client to call the service end point, create your header properties map and pass it in method parameter.
NotificationClient notificationClient = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(customDecoder())
.target(Target.EmptyTarget.create(NotificationClient.class));
Map<String, Object> headers = new HashMap<>();
headers.put("x-api-key", "x-api-value");
ResponseEntity<String> response = notificationClient.notify(new URI("https://stackoverflow.com/example"), headers, new NotificationBody());
A bit late to the game here, but if one needs an enforced, templated value, I discovered that this works in Spring Boot. Apparently, as long as the toString() gives a valid header value, you can use any type.
#FeignClient(
name = "my-feign-client",
url = "http://my-url.com"
)
public interface MyClient {
#GetMapping(
path = "/the/endpoint",
produces = MediaType.APPLICATION_JSON_VALUE
)
DataResponse getData(#RequestHeader(HttpHeaders.AUTHORIZATION) BearerHeader bearerHeader);
final class BearerHeader {
private final String token;
private BearerHeader(String token) {
this.token = token;
}
#Override
public String toString() {
return String.format("Bearer %s", token);
}
public static BearerHeader of(String token) {
return new BearerHeader(token);
}
}

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...

Spring MVC - Autowired field from header

I made web service with spring mvc(version 4).
This service used token in http header for authorization.
I want to value in http header bind to field in model class auto.
Is it possible? How can I do?
(See below code and comment)
Controller
#Controller
#RequestMapping(value = "/order")
public class OrderController {
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
#Autowired
private OrderService orderService;
#RequestMapping(value = "/")
#ResponseBody
public List<Order> getAll() throws Exception {
// I want to remove two line below with auto binding (userToken field in model)
// in all controller using token value
String token = request.getHeader("X-Auth-Token"); // remove~
orderService.setUserToken(token); // remove~
orderService.getAllbyUser()
return items;
}
}
Model(Service)
#Service
public class OrderService {
//#Autowired - is it possible?
private String userToken;
public String setUserToken(String userToken)
{
this.userToken = userToken;
}
public List<Order> getAllbyUser() {
String userId = userMapper.getUserId(userToken);
List<Order> list = orderMapper.getAllbyUser(userId);
return list;
}
}
#Autowire is for Spring to inject beans one to another. If you want to inject a String to a bean you can with the org.springframework.beans.factory.annotation.Value annotation.
For example:
#Value("${user.token}")
private String userToken;
This will make Spring search of the user.token in the VM args and other places (which I don't remember and in some specific order).
But again, as said in my initial comment, from the code you show here it seems to be an error setting this field as it is context specific and the #Service (by default) indicates that the OrderService is a singleton.
In order to read a header value from request, you can use #RequestHeader("X-Auth-Token") in your controller, as shown below:
#RequestMapping(value = "/")
#ResponseBody
public List<Order> getAll(#RequestHeader("X-Auth-Token") String token) throws Exception {
orderService.setUserToken(token); // remove~
orderService.getAllbyUser()
return items;
}
Hope this helps you.

Resources