I have two RestTemplate Beans, however sometimes when authRestTemplate is used get a 401 error (org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 : [no body]) I'm not sure why this is happening since it does not happen consistently.
This is how I have configured my RestTemplate.
#Configuration
#RequiredArgsConstructor
public class AppConfig {
private final FooConfig fooConfig;
#Bean
#LoadBalanced
public RestTemplate authRestTemplate(final RestTemplateBuilder restTemplateBuilder){
return restTemplateBuilder
.basicAuthentication(fooConfig.getUsername(), fooConfig.getPassword())
.build();
}
#Bean
#LoadBalanced
public RestTemplate basicRestTemplate(final RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.build();
}
}
If I am using the authRestTemplate it would look something like this
#Service
#RequiredArgsConstructor
public class authSubmissionServiceImpl implements CompletedApplicationService {
private final RestTemplate authRestTemplate;
private final FooConfig fooConfig;
#Override
public void doSomething() {
try {
Objects.requireNonNull(
authRestTemplate.getForObject(
String.format(somePath, fooConfig.getBaseUrl()),
String.class)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
I use constructor injection and use the same variable name as the RestTemplkete bean to differentiate between the two. Because of this, I don't need to use #Qualifier(). However, I know the authentication is being sent because 99% of the time the request goes through fine but there are spontaneous times where I get 401 [No Body] and I'm stumped. Any help would be greatly appreciated
Related
I have the following scenario:
A Service calls some rest endpoint to query a resource.
The result of this query should be cached for one day and then requeried.
To verify this behavior I wrote a test that calls the service method 2 times and expects that the rest service was actually only queried one time.
Here would be the class that calls the endpoint:
#EnableCaching
#Service
public class CachedResourceClient {
private final RestTemplate restTemplate;
public CachedResourceClient(#Value("${api.host}") String apiHost, RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder
.rootUri(apiHost)
.build();
}
#Cacheable("resource")
public Optional<Resource> fetchResource() {
try {
return Optional.of(restTemplate.getForObject("/resource", Resource.class));
} catch (HttpClientErrorException | HttpServerErrorException | UnknownHttpStatusCodeException e) {
// logging
return Optional.empty();
}
}
}
and then we have this test here:
#RestClientTest(CachedResourceClient.class)
public class CachedResourceClientTests {
#Autowired
private MockRestServiceServer server;
#Autowired
private CachedResourceClient cachedResourceClient;
#Test
public void fetchResource() throws JsonProcessingException {
server.expect(requestTo("/resource"))
.andRespond(withSuccess("{ resource: 'some' }", MediaType.APPLICATION_JSON));
cachedResourceClient.fetchResource();
cachedResourceClient.fetchResource();
server.verify();
}
}
As you can see, we're calling the fetchResource method two times. But the server.verify call fails, because there are also two requests sent to the mock server.
I think the RestClientTest does something behind the scene with my CachedResourceClient that conflicts with the way Caching is setup.
Because if I remove the RestClientTest-Annotation and manually mock the RestTemplateBuilder and the RestTemplate the tests work as expected.
But having a server mock is a bit more comfortable than manually mocking the used classes.
Is there something wrong with the setup?
I have a Java-Spring LIBRARY (NOT an application) which sends notifications via phone numbers. I'm using a Rest template to send a POST request. But instead of creating a new object of RestTemplate, I would want to use RestTemplateConfiguration #Configuration to do so.
IntelliJ version - 2020.2
Spring - 2.3.3.RELEASE
Java - 11
1st issue - When I'm trying to create a RestTemplateConfiguration class, I get the error -
Could not autowire. No beans of 'RestTemplateBuilder' type found.
#Configuration
public class RestTemplateConfiguration {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
2nd issue If I create a new object of RestTemplateBuilder, it doesn't give the error, but this is not how I want this class to behave. Also, this doesn't seem to work when in the class (mentioned below) SamplePhoneNumbers.java, I try to use RestTemplate restTemplate. It is coming as null.
#Configuration
public class RestTemplateConfiguration {
#Bean
public RestTemplate restTemplate() {
RestTemplateBuilder builder = new RestTemplateBuilder;
return builder.build();
}
}
Here is the class which sends notifications and where I am trying to use the Rest Template.
#Slf4j
public class SamplePhoneNumbers implements SampleClassStratergy {
RestTemplate restTemplate;
private static SamplePhoneNumbers samplePhoneNumbers = null;
private String phoneNumbersNotificationServiceURL = Enums.NotificationURL.enum_value + Enums.PhoneNumberApi.enum_value;
SamplePhoneNumbers() {
this.restTemplate = new RestTemplate();
}
public static SamplePhoneNumbers getInstance() {
if(samplePhoneNumbers ==null) {
samplePhoneNumbers = new SamplePhoneNumbers();
}
return samplePhoneNumbers;
}
#Async
public void sendNotification(String title, String message, List<String> listOfPhoneNumbers) {
SmsMessage smsMessage= new SmsMessage(title, message, listOfPhoneNumbers, Collections.emptyList(), Collections.emptyList());
try {
HttpEntity<SmsMessage> newRequest = new HttpEntity<>(smsMessage);
restTemplate.postForObject(phoneNumbersNotificationServiceURL, newRequest, String.class);
log.info("Notification sent via phone number.");
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
3rd issue: Is there any way I can get rid of getInstance() method so I don't have to handle Singleton logic? If I can create the class as a bean, that should work I guess, but I'm not sure how that can be achieved in this case.
Since this is a Library, it doesn't have a main method and no #SpringBootapplicationContext.
Can someone please assist me with the solution?
I also got the similar error while running Junit test case and resolved it by adding a bean of -
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder();
}
Do you want invoke your Java-Spring LIBRARY in a spring boot application? If so, you should add #ComponentScan in spring boot application main method as below.
pacakge com.prod
// for instance, Java-Spring LIRARY package is com.third
#SpringBootApplication
#ComponentScan(basePackages={"com.prod","com.third"})
public class Application {
Then you should do as below:
#Configuration
public class RestTemplateConfiguration {
#Bean(name="myRestTemplate")
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
#Slf4j
#Component
public class SamplePhoneNumbers implements SampleClassStratergy {
#Autowired
#Qualifier("myRestTemplate")
RestTemplate restTemplate;
#Async
public void sendNotification(String title, String message, List<String> listOfPhoneNumbers) {
SmsMessage smsMessage= new SmsMessage(title, message, listOfPhoneNumbers, Collections.emptyList(), Collections.emptyList());
try {
HttpEntity<SmsMessage> newRequest = new HttpEntity<>(smsMessage);
restTemplate.postForObject(phoneNumbersNotificationServiceURL, newRequest, String.class);
log.info("Notification sent via phone number.");
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
In this example of how to set up an Asynchronous service, for some reason the RestTemplate is set up in a very circuitous fashion.
Why can't the asynchronous routine itself just declare a new RestTemplate?
#Service
public class AsyncService {
private static Logger log = LoggerFactory.getLogger(AsyncService.class);
#Autowired
private RestTemplate restTemplate;
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Async("asyncExecutor")
public CompletableFuture<EmployeeNames> getEmployeeName() throws InterruptedException
{
log.info("getEmployeeName starts");
EmployeeNames employeeNameData = restTemplate.getForObject("http://localhost:8080/name", EmployeeNames.class);
log.info("employeeNameData, {}", employeeNameData);
Thread.sleep(1000L); //Intentional delay
log.info("employeeNameData completed");
return CompletableFuture.completedFuture(employeeNameData);
}
//...
Why can't the asynchronous routine itself just declare a new
RestTemplate?
Clearly no value here.
RestTemplate can be created simply with the new operator if not reused somewhere else.
Declaring it as #Bean makes sense if we want to reuse it somewhere else.
It indeed provides the singleton injectable/reusable in another bean that requires that.
But generally we don't do that in a #Service class like in this code but in a more global configuration class.
I'm using this configuration class to initialize RestTemplate:
#Configuration
public class RestTemplateConfig {
#Value("${endpoint-url}")
private String endpointUrl;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.rootUri(endpointUrl)
.messageConverters(new MappingJackson2HttpMessageConverter())
.build();
}
}
And in one of my service's method I use the code:
RootUriTemplateHandler handler = (RootUriTemplateHandler) restTemplate.getUriTemplateHandler();
String uri = handler.getRootUri();
restTemplate.postForLocation(uri, request);
To get this URI. Is there an easier method to get this rootUri (without casting)? Or to execute the post request directly to rootUri?
restTemplate.getUriTemplateHandler().expand("/")
You can execute the post request directly to rootUri, as long as the uri you provide to the restTemplate.postForLocation starts with "/". In that case, Spring will automatically add the baseURI provided in the restTemplate constructor.
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder,
#Value("${endpoint-url}") String endpointUrl) {
return builder
.rootUri(endpointUrl)
.messageConverters(new MappingJackson2HttpMessageConverter())
.build();
}
}
In your service's method:
// Make a POST call to ${endpoint-url}/foobar
String uri = "/foobar"; // The leading "/" is important!
restTemplate.postForLocation(uri, request);
Sounds like you are trying to use RestTemplate to pass along the value of ${endpoint-url}. That slightly awkward looking cast works but you could perhaps consider one of these alternatives:
Create a provider which encapsulates the endpointUrl and your restTemplate and inject this provider wherever you need either the endpointUrl or the restTemplate. For example:
#Component
public class RestTemplateProvider {
#Value("${endpoint-url}")
private String endpointUrl;
private final RestTemplate restTemplate;
#Autowired
public RestTemplateProvider(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.rootUri(endpointUrl)
.messageConverters(new MappingJackson2HttpMessageConverter())
.build();
}
public RestTemplate provide() {
return restTemplate;
}
public String getEndpointUrl() {
return endpointUrl;
}
}
Inject #Value("${endpoint-url}") private String endpointUrl; into which ever service class needs it.
EDIT: This question is specifically pertaining to the #RestClientTest annotation introduced in spring-boot 1.4.0 which is intended to replace the factory method.
Problem:
According to the documentation the #RestClientTest should correctly configure a MockRestServiceServer to use when testing a REST client. However when running a test I am getting an IllegalStateException saying the MockServerRestTemplateCustomizer has not been bound to a RestTemplate.
Its worth noting that I'm using Gson for deserialization and not Jackson, hence the exclude.
Does anyone know how to correctly use this new annotation? I haven't found any examples that require more configuration then when I have already.
Configuration:
#SpringBootConfiguration
#ComponentScan
#EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class})
public class ClientConfiguration {
...
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.rootUri(rootUri)
.basicAuthorization(username, password);
}
}
Client:
#Service
public class ComponentsClientImpl implements ComponentsClient {
private RestTemplate restTemplate;
#Autowired
public ComponentsClientImpl(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
public ResponseDTO getComponentDetails(RequestDTO requestDTO) {
HttpEntity<RequestDTO> entity = new HttpEntity<>(requestDTO);
ResponseEntity<ResponseDTO> response =
restTemplate.postForEntity("/api", entity, ResponseDTO.class);
return response.getBody();
}
}
Test
#RunWith(SpringRunner.class)
#RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {
#Autowired
private ComponentsClient client;
#Autowired
private MockRestServiceServer server;
#Test
public void getComponentDetailsWhenResultIsSuccessShouldReturnComponentDetails() throws Exception {
server.expect(requestTo("/api"))
.andRespond(withSuccess(getResponseJson(), APPLICATION_JSON));
ResponseDTO response = client.getComponentDetails(requestDto);
ResponseDTO expected = responseFromJson(getResponseJson());
assertThat(response, is(expectedResponse));
}
}
And the Exception:
java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate
Answer:
As per the answer below there is no need to declare a RestTemplateBuilder bean into the context as it is already provided by the spring-boot auto-configuration.
If the project is a spring-boot application (it has #SpringBootApplication annotation) this will work as intended. In the above case however the project was a client-library and thus had no main application.
In order to ensure the RestTemplateBuilder was injected correctly in the main application context (the bean having been removed) the component scan needs a CUSTOM filter (the one used by #SpringBootApplication)
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)
})
The MockRestServiceServer instance should be constructed from the static factory, using a RestTemplate. See this article for a detailed description of the testing process.
In your example, you can do:
#RunWith(SpringRunner.class)
#RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {
#Autowired
private ComponentsClient client;
#Autowired
private RestTemplate template;
private MockRestServiceServer server;
#Before
public void setUp() {
server= MockRestServiceServer.createServer(restTemplate);
}
/*Do your test*/
}
You have RestTemplateBuilder at two places. At ClientConfiguration class and at ComponentsClientImpl class. Spring boot 1.4.0 auto-configure a RestTemplateBuilder which can be used to create RestTemplate instances when needed. Remove below code from ClientConfiguration class and run your test.
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.rootUri(rootUri)
.basicAuthorization(username, password);
}