0

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.*"})
Octavia
  • 198
  • 1
  • 13

1 Answers1

1

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);
        [...]
  }   
}
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • well I tried to put it as a global variable Autowired TokenReceiver tokenReceiver; but it complains that "Autowired members must be defined in valid Spring bean (Component|Service|...) " even if TokenReceiver is annotated with Component – Octavia Dec 19 '22 at 09:22
  • Sound like this error is intelliJ specific or something... In general, spring should managed all the beans into which you want to inject something, that's how it works :) So `MyWebServiceCaller` class from my answer also has to be defined as a spring bean so that spring will be able to find this component during component scanning process. – Mark Bramnik Dec 19 '22 at 09:29
  • global variable? The error concerns where you defined Autowired TokenReceiver tokenReceiver not the fact that TokenReceiver is annotated Component or not. But it looks like your classes are not scanned by Spring: are they in a package respecting the default convention of Spring Boot? If not you have to ask Spring to scan them in your @ Configuration. (... and then the restTemplate in TokenReceiver should also @Autowired) – p3consulting Dec 19 '22 at 09:32
  • thanks for your answers, I also marked with **Component** the class from which the call was made, and this class is called from a **RestController**. But now that I think, probably this Proxy Class (I updated the code in post) should also be autowired in the rest controller... – Octavia Dec 19 '22 at 10:03
  • I marked your answer as correct, because it made me realize that everything must be autowired in chain, because, obviously, if at some point a new object is made, that all the injected data will not be in the new object. Thanks for help! – Octavia Dec 19 '22 at 10:12