calling method with Rest Template Builder - java

I created this rest template with the rest template builder and set connection and read timeouts. I need to call this rest template from other methods in the program, but am unsure how to do so. please help, thanks in advance!
//create rest template with rest template builder and set connection and read timeouts #Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(connectTimeout))
.setReadTimeout(Duration.ofMillis(readTimeout))
.build();
}
// this is an example method that calls rest template, unsure what goes in the parameter section
#Bean
public example example() {
return new restTemplate(what goes here)
);
}

if you have created your customised RestTemplate, you may autowire it any class where you want to call it and use the one. if you more than 1 RestTemplates you can use #Qualifier above RestTemplate Bean and use the same in the calling class.

RestTemplateBuilder is a bean provided by Spring boot. You can inject that into any of your Spring bean classes.
Then you just want to configure your restTemplate at the creation of your Spring bean class and store it as a field. You can do something like below (This is not the one and only way).
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
#Configuration
public class MyExampleRestClientConfiguration {
private final RestTemplateBuilder restTemplateBuilder;
#Autowired
public MyExampleRestClient(RestTemplateBuilder restTemplateBuilder) {
this.restTemplateBuilder = restTemplateBuilder;
}
#Bean
public RestTemplate restTemplate() {
return restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(connectTimeout))
.setReadTimeout(Duration.ofMillis(readTimeout))
.build();
}
}
Now in Some other spring bean class, you can simply wire the restTemplate bean and re-use.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class MyExampleRestClient {
private final RestTemplate restTemplate;
#Autowired
public MyExampleRestClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
//Now You can call restTemplate in any method
}
You may refer this for more.

Related

Spring Annotations Import Config not called

I am trying to make an application that uses Spring annotations to import the configurations. For this question i narrowed it down to two files. The Startup class:
package core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Slf4j
#Configuration
#Import(ConfigSettings.class)
public class Startup {
public static void main (String args[]) {
log.info("main class");
}
}
and the ConfigSettings
package core;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Slf4j
#Configuration
#ComponentScan({"connections", "filter"})
#PropertySource({"classpath:config/${env.config:dev}.application.properties"})
public class ConfigSettings {
public ConfigSettings() {
log.info("Constructor ConfigSettings");
}
}
I expected the outcome to be:
[INFO]Constructor ConfigSettings
[INFO]main class
But it only shows mainclass. It looks like the constructor of the config settings is not called at all. I expect it to call it because of the import annotation.
Can anyone explain what is going wrong? Thank you in advance!
Your best bet is to make the config class return config object that contains your values. Generally I don't tend to add an all-encompassing config object, but have a config file for each component (database, controllers, etc...).
You can then return the configured object as a bean and let spring inject it. If I were to make a config file for a RestTemplate (as a simple example):
#Service
public class RestClientConfig {
#Value("${your.config.value}")
private String yourValue;
private final RestTemplate restTemplate = new RestTemplate();
#Bean
public RestTemplate restTemplate() {
// Configure it, using your imported values
// ...
return restTemplate;
}
}
However, the main method is outside of the spring container and you won't be able to bootstrap it that way, but with the above method you can call the configured component directly where you need to use it.

Create #MockBean with qualifier by annotating class?

In my Spring Boot test I'm using 2 mock beans with different qualifiers:
#RunWith(SpringRunner.class)
#SpringBootTest
class HohoTest {
#MockBean #Qualifier("haha") IHaha ahaha;
#MockBean #Qualifier("hoho") IHaha ohoho;
}
Since I'm not using these beans explicitly, I would rather move them away from the class body, as the #MockBean annotation is now repeatable:
#RunWith(SpringRunner.class)
#SpringBootTest
#MockBean(IHaha.class)
#MockBean(IHaha.class)
class HohoTest {}
However, I need to pass in a qualifier as well, since they have the same type. Any idea on how I can achieve that?
Because using annotation #Qualifier means choose bean by name, so you can set up a name for a mock with code like this:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {JsonMapperConfig.class})
public class IntegrationFlowTest {
#MockBean(name = "s3MessageRepository")
private S3Repository s3MessageRepository;
// etc
If it is okay to move the mock definition completely out of the test class, you could also create the mocks in a separate #Configuration class:
#Configuration
public class MockConfiguration
{
#Bean #Qualifier("haha")
public IHaha ahaha() {
return Mockito.mock(IHaha.class);
}
#Bean #Qualifier("hoho")
public IHaha ohoho() {
return Mockito.mock(IHaha.class);
}
}
When declaring #MockBean at the class level, there is currently no support for providing a qualifier.
If you would like to have such support, I suggest you request it in the Spring Boot issue tracker.
Otherwise, you will need to continue declaring #MockBean on fields alongside #Qualifier.
I had a similar requirement of injecting mocked service beans with #Order annotation. I also needed to verify the invocation count of service functions. Below is my implementation. It might help someone.
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceNameTest {
#Autowired private ServiceName serviceName;
// Important: Used to reset interaction count of our static
// bean objects before every test.
#Before
public void reset_mockito_interactions() {
Mockito.clearInvocations(MockServicesConfig.bean1);
Mockito.clearInvocations(MockServicesConfig.bean2);
}
#Configuration
public static class MockServicesConfig {
public static InterfaceName bean1;
public static InterfaceName bean2;
#Bean
#Order(1)
public InterfaceName bean1() {
bean1 = Mockito.mock(InterfaceName.class);
// Common when() stubbing
return bean1;
}
#Bean
#Order(2)
public InterfaceName vmpAdapter() {
bean2 = Mockito.mock(InterfaceName.class);
// Common when() stubbing
return bean2;
}
}
#Test
public void test_functionName_mock_invocation1() {
// Arrange --> Act --> Assert
// nullify other functions custom when() stub.
// updating this functions custom when() stub.
verify(MockServicesConfig.bean1, times(1)).functionName("");
}
#Test
public void test_functionName_mock_invocation2() {
// Arrange --> Act --> Assert
// nullify other functions custom when() stub.
// updating this functions custom when() stub.
verify(MockServicesConfig.bean1, times(1)).functionName("");
}
}
This should now work
#SpringBootTest(
classes = Some.class
)
#MockBean(name = BEAN_NAME, classes = TheBeanClass.class)
#MockBean(name = BEAN_NAME_2, classes = TheBeanClass.class)
class SomeTest {
private final Some some;
#Autowired
SomeTest(Some some) {
this.some = some;
}
}
Please note, if you need to use any of the mocked beans, you will have to put the #Qualifier in the constructor, for example
private final TheBeanClass theBeanclass;
private final Some some;
#Autowired
SomeTest(Some some, #Qualifier(BEAN_NAME) TheBeanClass theBeanClass) {
this.some = some;
this.theBeanClass = theBeanClass;
}

How setup JMS without Spring, I'm using Google Guice

I'm facing some problems trying to setup JMS without spring boot, before I was using Spring and everything was working fine, but now I won't use it anymore, I'm using Google Guice for some reasons.
I configured that Jms class config to inject the things that it requires
org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.util.ErrorHandler;
import org.springframework.util.backoff.FixedBackOff;
import javax.jms.ConnectionFactory;
#Configuration
#EnableJms
public class JmsConfig {
private static final String JMS_TYPE_ID_PROPERTY_NAME = "_type";
#Bean
public JmsListenerContainerFactory<?> jmsQueueFactory(
ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
log.info("JmsConfig.jmsQueueFactory - (TraceTest1)");
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all Spring Boot's defaults to this factory, including message converter
configurer.configure(factory, connectionFactory);
factory.setErrorHandler(new JmsErrorHandler());
factory.setMessageConverter(jacksonJmsMessageConverter());
//retry indefinitely every 60 seconds
factory.setBackOff(new FixedBackOff(60000, FixedBackOff.UNLIMITED_ATTEMPTS));
return factory;
}
public static class JmsErrorHandler implements ErrorHandler {
private static final Logger log = LoggerFactory.getLogger(JmsErrorHandler.class);
#Override
public void handleError(Throwable t) {
log.error("JMS error", t);
}
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
log.info("JmsConfig.jacksonJmsMessageConverter - (TraceTest2)");
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName(JMS_TYPE_ID_PROPERTY_NAME);
return converter;
}
}
The problems is, now that I'm not using Spring this class and respective beans does not initialize during the application start.
I have another method in another class, kind of a Main that has a "start method", I want to abstract this to call it there without spring but I could not find implementation of it manually using Guice, could you guys help me?
Thanks!

How to configure #Component class appropriately

I have a SqsQueueSender to send messages to AWS. I want to test this class. My thought is that it should be a #Component that is injected in to the classes that need it. Importantly, I want to configure the endpoint of the SqsQueueSender to be different in testing vs. production environments.
I've been moving #Autowired and #Component around the classes various different ways but must have some basic misunderstanding. Here's my latest configuration:
package com.foo.fulfillmentApi.dao;
import com.amazonaws.services.sqs.AmazonSQSAsyncClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.messaging.support.MessageBuilder;
#Component
public class SqsQueueSender {
private static final Logger LOGGER = LoggerFactory.getLogger(SqsQueueSender.class);
private final QueueMessagingTemplate queueMessagingTemplate;
#Autowired
AmazonSQSAsyncClient amazonSQSAsyncClient;
//This value is from /resources/application.properties
private #Value("${sqs.endpoint}") String endpoint;
public SqsQueueSender(AmazonSQSAsyncClient amazonSqsAsyncClient) {
amazonSqsAsyncClient.setEndpoint(endpoint);
this.queueMessagingTemplate = new QueueMessagingTemplate(amazonSqsAsyncClient);
}
public void send(String queueName, String message) {
this.queueMessagingTemplate.convertAndSend(queueName, MessageBuilder.withPayload(message).build());
}
}
The error message on startup states
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.amazonaws.services.sqs.AmazonSQSAsyncClient' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations: {}
To implement SqsQueueSender you must pass an AmazonSQSAsyncClient. How do I make sure this component can access an existing bean of that type?
You need to create a configuration class. In your case it would be something like this:
#Configuration
public class AWSConfig {
#Bean(name ="awsClient")
public AmazonSQSAsync amazonSQSClient() {
AmazonSQSAsyncClient awsSQSAsyncClient
= new AmazonSQSAsyncClient();
// set up the client
return awsSQSAsyncClient;
}
If it has problems with injecting then add qualifier in qsQueueSender:
#Autowired
#Qualifier("awsClient")
AmazonSQSAsyncClient amazonSQSAsyncClient;
You can also do this using the xml configuration but as you are using annotations then this is more advisable approach.
Add #Component/#Service in com.amazonaws.services.sqs.AmazonSQSAsyncClient or return an object of that using #Bean annotation from configuration class.
If you use springboot - define into your startup application file as below
#Bean
public AmazonSNSAsync amazonSNSClient() {
ClientConfiguration config = new ClientConfiguration();
return AmazonSNSAsyncClient.asyncBuilder().withClientConfiguration(config)
.withRegion(Regions.fromName(region))
.withCredentials(new DefaultAWSCredentialsProviderChain())
.build();
}

why properties file can not be imported

The properties file is src\main\resources\exam-binary.properties. The content in exam-binary.properties is:
user.post.url=http://localhost:9000/users/newUser
The import class is as below, however, seems that the value can not be imported.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.client.RestTemplate;
import com.ma2oo.model.domain.User;
#Configuration
#PropertySource("classpath:exam-binary.properties")
public class RegisterUser {
private static final RestTemplate restTemplate = new RestTemplate();
#Value("${user.post.url}")
private String registerUrl;
public User Register(final User user) {
System.out.println("url print: " + registerUrl);
return restTemplate.postForObject(registerUrl, user, User.class);
}
}
I have #EnableAutoConfiguration which would cover all classes. And The method which would call RegisterUser is:
#RequestMapping(value = {"/signUp"}, method = RequestMethod.POST)
public ModelAndView signUp(#ModelAttribute("user") User user) {
new RegisterUser().Register(user);
return new ModelAndView("quiz_start");
}
The standard output is:
url print: null
Could anyone help that why #PropertySource does not work ?
Thanks in advance.
The root cause is that the instance of RegisterUser that you are using is not a Spring managed bean. Instead, you created it yourself
new RegisterUser() // spring has no knowledge of it
Why is this class a Configuration class? You seem to be using it as a service. Move the property configuration to a proper #Configuration class, declare a bean of type RegisterUser and use the bean in your #RequestMapping annotated method.

Categories

Resources