2

I am new to Spring. I develop Service that Consuming RESTful service with certficate using Java

Here is my Config class:

package configuration;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.util.function.Supplier;

@Configuration
public class RestClientCertConfig {

    private char[] allPassword = "allpassword".toCharArray();

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {

        SSLContext sslContext = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword, allPassword)
                .loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword)
                .build();

        HttpClient client = HttpClients.custom()
                .setSSLContext(sslContext)
                .build();

        return builder
                .requestFactory((Supplier<ClientHttpRequestFactory>)new HttpComponentsClientHttpRequestFactory(client))
                .build();
    }
}

And here is the class where I consume Restful EndPoint:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.net.URISyntaxException;
import java.util.Collections;


public class ECSConfigGet {
    private static final String ECS_API_URI = "<RestEndPointToConsume";

    @Autowired
    private static RestTemplate restTemplate;


    public static void main(String[] args) {
        try {
            makeECSCall("myTestHeaderValue");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }


    private static void makeECSCall(String entityCode) throws RestClientException, URISyntaxException {
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.set("entityCode", entityCode);

        HttpEntity<String> entity = new HttpEntity<>("parameters", headers);

        ResponseEntity responseEntity  = restTemplate.exchange(ECS_API_URI, HttpMethod.GET, entity, String.class);
        }
    }

Did I completely misunderstood the concept? I would expect restTemplate would not be null with all the Annotations I use. Thank for any help!


NullPointerException is fixed. ECSConfigGet looks like:

package main;

import configuration.RestClientCertConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import services.modelsdto.ExpenseConfigDTO;


import java.util.Collections;

@SpringBootApplication
@Component
public class ECSConfigGet implements CommandLineRunner{

    //API to call
    private static final String ECS_API_URI = "<API_TO_CONSUME>";
    @Autowired
    private RestTemplate restTemplate;

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RestClientCertConfig.class);
        applicationContext.getBean(RestTemplate.class);
        SpringApplication.run(ECSConfigGet.class, args);

    }

    private void makeECSCall(String entityCode) throws RestClientException {

        ExpenseConfigDTO expenseConfigDTO = new ExpenseConfigDTO();

        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.set("entityCode", entityCode);

        HttpEntity<String> entity = new HttpEntity<>("parameters", headers);

        ResponseEntity responseEntity  = restTemplate.exchange(ECS_API_URI, HttpMethod.GET, entity, String.class);
        }

    @Override
    public void run(String... args) {
        for (int i = 0; i < args.length; ++i) {
            makeECSCall("myTestHeaderValue");
        }
    }
}
  • 5
    *Did I completely misunderstood the concept?* Yes, I'm afraid. First, your configuration class needs to be scanned. Second, a bean (RestTemplate) can only be autowired into another bean, but ECSConfigGet is not a bean. Third: what can be autowired is **instance** fields, not static fields. Read the documentation, because you're missing core, fundamental principles (and because it's always a good idea to read the documentation anyway). – JB Nizet Aug 08 '18 at 22:20
  • This is a good example of why it's better to wire your class via its constructor instead of direct field access. – chrylis -cautiouslyoptimistic- Aug 08 '18 at 23:45

2 Answers2

5

You're missing a bit of Spring boilerplate that you need to make @Autowired work. If you're using Spring Boot, you're close, but @Patrick is right generally: ECSConfigGet needs to be a bean by annotating it correctly, but you also need to run your application within an application context in order for any of the Spring magic to happen. I suggest checking out this tutorial on how to use Spring Boot in a command line application.

The high level is ECSConfigGet needs to be annotated with @SpringBootApplication and then have it implement CommandLineRunner and then from the run method, you will have access to the @Autowired component. Spring will instantiate ECSConfigGet and populate the properties. Also as @Roddy pointed out, RestTemplate cannot be static, either.

Chris Thompson
  • 35,167
  • 12
  • 80
  • 109
  • Can you even autowire a static variable? – Roddy of the Frozen Peas Aug 08 '18 at 22:20
  • Ha. Good point. It also needs to be nonstatic – Chris Thompson Aug 08 '18 at 22:21
  • @RoddyoftheFrozenPeas not sure if your question was rhetorical or not, but somebody has asked that exact question! https://stackoverflow.com/questions/1018797/can-you-use-autowired-with-static-fields (the answer is no, modulo creating a setter on an instance that does it, but that's pretty dirty) – Chris Thompson Aug 08 '18 at 22:24
  • (It was rhetorical. ;) But hopefully the linked question will help the OP's understanding.) – Roddy of the Frozen Peas Aug 08 '18 at 22:26
  • @ChrisThompson, Thanks! I added **@SpringBootApplication**, **@Component** for ECSConfigGet, it implements CommandLineRunner and **RestTemplate is not static** (please see the updated class above). I also created **application.properties** file in src.resources with property **spring.main.web-application-type=NONE**. Also, added **@ComponentScan ("main")** for RestClientCertConfig class. Now Application Failed to start: **"Field restTemplate in main.ECSConfigGet required a bean of type 'org.springframework.web.client.RestTemplate' that could not be found."** Any tip what is still wrong? – Katsiaryna Piotukh Aug 09 '18 at 22:28
  • I fixed the problem by re-structure the project (I put my configuration package under main package where ECSConfigGet is). Thanks again – Katsiaryna Piotukh Aug 10 '18 at 13:02
0

The ECSConfigGet class is not a bean so it can not autowire a component. Add @Component as class annotation to ECSConfigGet

Mehtrick
  • 518
  • 3
  • 12