How to use the method amazonSQSClient() in the main class? - java

I am new in Spring Boot. Does anyone know how to call the method amazonSQSClient() in the main class?
#Configuration
public class AWSConfiguration {
#Value("${aws.access.key.encrypted}")
private String amazonAWSAccessKey;
#Value("${aws.secret.key.encrypted}")
private String amazonAWSSecretKey;
#Value("${aws.region.encrypted}")
private String amazonAWSRegion;
#Bean
public AmazonSQS amazonSQSClient() {
AWSCredentials awsCredentials = new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey);
AmazonSQS client = AmazonSQSClientBuilder.standard()
.withRegion(amazonAWSRegion)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).build();
return client;
}
}

You shouldn't need to. That's the point of using the #Bean annotation, Spring will inject an instance of AmazonSQS whenever you need one. So, you might have a class like this that uses the SQS client:
#Service
public class QueueService {
private AmazonSQS amazonSQS;
#Autowired
public QueueService(AmazonSQS sqs) {
this.amazonSQS = sqs;
}
}
The Spring framework is smart enough to see that it needs to inject an instance of AmazonSQS, and by marking the method amazonSqsClient with the #Bean annotation, you are telling Spring to use that method whenever it needs to create an instance of AmazonSQS.

Related

Spring: Implement two instances of a #Service with two instances of #Configuration linked to each respective service

An existing utility exists that I am importing to my project. It is written similarly as these two classes
#Service
public class ServiceAccessorImpl implements ServiceAccessor {
#Autowired private ServiceConfiguration serviceConfiguration;
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
#Configuration
#Data //automatically adds getters and setters
#ConfigurationProperties(prefix="config")//pulls serviceEndPoint value from external config
//Assume external config has config.serviceEndPoint = "www.endpoint1.com"
public class ServiceConfiguration {
private String serviceEndPoint;
}
In a separate project below I am importing the above into my project. I would like to have two instances of the same service with two unique and respective configuration classes. so that service1 is linked to config1 and service2 is linked to config2. My reasoning is I want one instance that only pulls the endpoint from the external configuration and another instance that I can use to set the endpoint. I have tried using things like #Qualifier but I cant figure out how to link the correct config with the correct service. I have a feeling that this may not be possible because ServiceConfiguration is privately scoped within ServiceAccessorImpl and I have no access through setters or constructors.
Controller Endpoint. The below is psuedo code of how I would like to implement my design. Autowiring in a single instance and using either endpoint works for that endpoint but not for both as shown below.
#ComponentScan(basePackageClass = ServiceAccessorImpl.class)
public class ServiceAccessorController {
#Autowired private ServiceAccessor serviceAccessor1;
#Autowired private ServiceConfiguration serviceConfiguration1;
#Autowired private ServiceAccessor serviceAccessor2;
#Autowired private ServiceConfiguration serviceConfiguration2;
Response CallEndpoint1(){
//www.endpoint1.com is already set here from external config
return serviceAccessor1.executeCall();
}
Response CallEndpoint1(){
serviceConfiguration2.setServiceEndPoint("www.endpoint2.com")
return serviceAccessor2.executeCall();
}
}
Thank you in advance
If you need multiple instances of the same implementation, it's easier to not annotate it as a bean, and have a #Configuration provide the beans instead.
public class ServiceAccessorImpl implements ServiceAccessor {
private ServiceConfiguration serviceConfiguration;
public ServiceAccessorImpl(ServiceConfiguration configuration) {
this.serviceConfiguration = configuration;
}
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
// this one should just have #ConfigurationProperties, not #Configuration
#Data
#ConfigurationProperties(prefix="config")
public class ServiceConfiguration {
private String serviceEndPoint;
}
then in your service you can have a Configuration providing both instances:
#Configuration
public class BeansConfiguration {
#Qualifier("service1")
#Primary // optional, Spring will autowire this instance by default if no qualifier is given
#Bean
public service1(#Autowired ServiceConfiguration config) {
// use the default config bean
return new ServiceAccessorImpl(config);
}
#Qualifier("service2")
#Bean
public service2() {
return new ServiceAccessorImpl(new ServiceConfiguration("www.endpoint2.com"));
}
}
then you may consume both by using the qualifiers (note that you don't have to inject the configs here):
public class ServiceAccessorController {
#Autowired
private ServiceAccessor serviceAccessor1;
#Autowired
#Qualifier("service2")
private ServiceAccessor serviceAccessor2;
Response CallEndpoint1(){
return serviceAccessor1.executeCall();
}
Response CallEndpoint2(){
return serviceAccessor2.executeCall();
}
}

Mocking an OpenFeign client for Unit Testing in a spring library and NOT for a spring boot application

I've implemented a feign client that calls a get API based on this official repository. I have a rule class UserValidationRule that needs to call that get API call getUser() and validate some stuff. That works as expected but when I get to testing that rule class, mocking the feign client is not successful and it continues to call the actual API. I've simplified the situation so please ignore the simplicity lol. This is a follow up question I have after i found this stackoverflow question
The API returns this model:
#Data
public class userModel {
private long id;
private String name;
private int age;
}
The interface with the rest client method:
public interface UserServiceClient {
#RequestLine("GET /users/{id}")
UserModel getUser(#Param("id") int id);
}
And in the rule class, i build the feign client and call the API:
#RequiredArgsConstructor
#Component
public class UserValidationRule {
private static final String API_PATH = "http://localhost:8080";
private UserServiceClient userServiceClient;
public void validate(String userId, ...) {
// some validations
validateUser(userId);
}
private void validateUser(String userId) {
userServiceClient = getServiceClient();
UserModel userModel = userServiceClient.gerUser(userId);
// validate the user logic
}
}
private UserServiceClient getServiceClient() {
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(UserServiceClient.class, API_PATH);
}
}
And here comes the test class:
public class UserValidationRuleTest {
private UserServiceClient userServiceClient = mock(UserServiceClient.class);
private UserValidationRule validationRule = new UserValidationRule();
private UserModel userModel;
#Before
public void init() {
userModel = generateUserModel();
}
#Test
public void validateWhenAgeIsNotBlank() {
doReturn(userModel).when(userServiceClient).getUser(any());
validationRule.validate("123", ...);
// some logic ...
assertEquals(.....);
verify(userServiceClient).getUser(any());
}
private UserModel generateUserModel() {
UserModel userModel = new UserModel();
userModel.setName("Cody");
userModel.setAge("22");
return accountModel;
}
}
As I debug validateWhenAgeIsNotBlank(), i see that the userModel is not the one that's generated in the test class and the values are all null. If I pass in an actual userId, i get an actual UserModel that I have in my db.
I think the problem is that UserServiceClient is not being mocked. The verify is failing as it says the getUser() is not invoked. It might be something to do with how the feign client is declared in the UserValidationRule with the feign.builder()...
Please correct me if I'm wrong and tell me what I'm missing or any suggestions on how to mock it correctly.
You are not using the spring managed UserServiceClient bean. Every time you call UserValidationRule.validate it calls validateUser which in turn calls the getServiceClient method. This getServiceClient creates a new instance of UserServiceClient for each invocation. This means when testing the mocked UserServiceClient is not in use at all.
I would restructure the code as below;
First either declare UserServiceClient as final with #RequiredArgsConstructor or replace #RequiredArgsConstructor with #AllArgsConstructor. The purpose of this change is to allow an instance of UserServiceClient be injected rather than creating internally in the service method.
#Component
#RequiredArgsConstructor
public class UserValidationRule {
private final UserServiceClient userServiceClient;
.... // service methods
}
Then have a separate Configuration class that builds the feign client as spring bean;
#Bean
private UserServiceClient userServiceClient() {
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(UserServiceClient.class, API_PATH);
}
At runtime this bean will now be injected into the UserValidationRule.
As for the unit test changes you are creating the mock correctly but aren't setting/injecting that mock anywhere. You either need to use #Mock and #InjectMocks annotations or manually create instance of UserValidationRule in your #Before method.
Here is how #Mock and #InjectMocks use should look like;
#RunWith(MockitoJUnitRunner.class)
public class UserValidationRuleTest {
#Mock private UserServiceClient userServiceClient;
#InjectMocks private UserValidationRule validationRule;
... rest of the code
or continue using mock(...) method and manually create UserValidationRule.
public class UserValidationRuleTest {
private UserServiceClient userServiceClient = mock(UserServiceClient.class);
private UserValidationRule validationRule;
private UserModel userModel;
#Before
public void init() {
validationRule = new UserValidationRule(userServiceClient);
userModel = generateUserModel();
}
... rest of the code
This will now ensure you are using single instance of spring managed feign client bean at runtime and mocked instance for the testing.

Spring Feign configuration: how to test all #Bean methods are called

I want to configure Spring feign with a configuration class, and I want to make sure that all the #Bean methods are called when Spring configures the feign client for me.
How to test it?
For example, I have:
#FeignClient(
name = "PreAuthSendRequest",
url = "${xxx.services.preauth.send.url}",
configuration = AppFeignConfig.class)
public interface RequestService {
#PostMapping("")
#Headers("Content-Type: application/json")
PreAuthResponse execute(#RequestBody PreAuthRequest preAuthRequest);
}
And AppFeignConfig.java:
#Configuration
#RequiredArgsConstructor
public class AppFeignConfig{
private final HttpClient httpClient;
private final Jackson2ObjectMapperBuilder contextObjectMapperBuilder;
#Bean
public ApacheHttpClient client() {
return new ApacheHttpClient(httpClient);
}
#Bean
public Decoder feignDecoder() {
return new JacksonDecoder((ObjectMapper)contextObjectMapperBuilder.build());
}
#Bean
public Encoder feignEncoder() {
return new JacksonEncoder((ObjectMapper)contextObjectMapperBuilder.build());
}
#Bean
public Retryer retryer() {
return Retryer.NEVER_RETRY;
}
#Bean
public ErrorDecoder errorDecoder() {
return new ServiceResponseErrorDecoder();
}
}
So, how to verify that all #Bean methods are called? I know #MockBean, but what I want to check is config.feignDecoder(), etc., are indeed called.
When I am trying to context.getBean(RequestService.class); and call execute() method, it seems to send a real request and without wiremock, it fails, obviously.
For now I have this:
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
class RequestServiceTest {
#Autowired
private ApplicationContext applicationContext;
#MockBean
private ApacheHttpClient client;
#MockBean
private Decoder feignDecoder;
#MockBean
private Encoder feignEncoder;
#MockBean
private Retryer retryer;
#MockBean
private ErrorDecoder errorDecoder;
#Test
void shouldRetrieveBeansFromApplicationContextToConstructConfigurationInstance() {
AppFeignConfig config = applicationContext.getBean(AppFeignConfig.class);
Assertions.assertEquals(config.feignEncoder(), feignEncoder);
Assertions.assertEquals(config.feignDecoder(), feignDecoder);
Assertions.assertEquals(config.errorDecoder(), errorDecoder);
Assertions.assertEquals(config.client(), client);
Assertions.assertEquals(config.retryer(), retryer);
}
}
I don't know if it is how it should be. If any idea, please comment.

Not able to access Spring boot application.properties

I am trying to access application.properties values in spring boot application's service class. But every time value is null, so it throws NullPointException. I can get the right value for port in controller class(if i add #Autowired in controller) but not in service class. What changes are required to make this properties available through out the application?
Controller looks like:
#RestController
public class MyController {
MyService ss = new MyService();
#RequestMapping(value = "/myapp/abcd", method = RequestMethod.POST, consumes = {"application/json"})
public ResponseMessage sendPostMethod(#RequestBody String request) {
log.debug(" POST Request received successfully; And request body is:"+ request);
ResponseMessage response = ss.processRequest(request);
return response;
}
}
And Service class is:
#Service
public class MyService {
private ApplicationProperties p;
#Autowired
public setProps(ApplicationProperties config) {
this.p = config;
}
public ResponseMessage processRequest(String request) {
System.out.println("Property value is:"+ p.getPort());
}
}
ApplicationProperties.java looks like:
#Component
#Getter
#Setter
#ConfigurationProperties
public class ApplicationProperties {
String port;
}
Finally, application.properties file has:
port=1234
I have even tried passing ApplicationProperties instance from controller to service's default constructor, but it did not work. When I debug, value persist while application startup, but when I make a rest web service POST call, it is null.
In your controller MyController, you have to inject the service, replacing:
MyService ss = new MyService();
by:
#Autowired
MyService ss;
Also, instead of your ApplicationProperties class, you can use #Value annotation, from Spring, to load properties from application.properties file. Take a look at this code:
import org.springframework.beans.factory.annotation.Value;
// ...
#Service
public class MyService {
#Value("${port}")
private String port;
// ...
}
I had the same issue but for other reasons.
For me the solution was to add spring. before each parameter in application.properties.
So e.g. "spring.flyway.user=root".

Why spring automatically #Autowitred fields in non-managed classes

As far as I know spring provides some ways to inject beans into non-managed classes.
It can be done explicitly with AutowireCapableBeanFactory. (How to inject dependencies into a self-instantiated object in Spring?)
But I've faced strange (IMHO) behavior, when spring performs such injection automatically.
Here is an example with spring batch,
Configuration:
#SpringBootConfiguration
public class ProcessorJobConfig {
//.....
#Bean(name = "pullRestTemplate")
public RestTemplate createPullRestTemplate() {
RestTemplate restTemplate = restTemplateBuilder.build();
return restTemplate;
}
#Bean(name = "step")
public Step step(#Autowired ItemReader<Measurement> itemReader,
#Autowired ItemProcessor<Measurement, Event> itemProcessor,
#Autowired ItemWriter<Event> itemWriter) {
return stepBuilderFactory.get("step")
.<Measurement, Event>chunk(Integer.MAX_VALUE)
.reader(itemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
}
#Bean(name = "restProcessorJob")
public Job job(#Qualifier("step") Step step) throws Exception {
return jobBuilderFactory.get("restProcessorJob")
.start(step)
.build();
}
#Bean
public ItemReader<Measurement> itemReader() {
RestMeasureReader restMeasureReader = new RestMeasureReader(); // Use new() explicitly
return restMeasureReader;
}
//.....
}
Reader:
public class RestMeasureReader implements ItemReader<Measurement> {
private static final Logger LOGGER = LoggerFactory.getLogger(RestMeasureReader.class);
/**
* NOTE: This field will be injected automatically by spring, even we are using new() to create instance of this class.
*/
#Autowired
#Qualifier("pullRestTemplate")
private RestTemplate restTemplate;
#Override
public Measurement read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
// do some stuff
}
}
And application itself
#EnableBatchProcessing
#EnableTask
#SpringBootApplication
public class TestAutowiredTaskApplication {
public static void main(String[] args) {
SpringApplication.run(TestAutowiredTaskApplication.class, args);
}
}
Even I use explicit new() to instantiate RestMeasureReader, its RestTemplate field will be injected afterwards.
Is it normal behavior? I do not expect spring to automatically inject fields when creating object with new().
If you are talking about using new inside of your #Configuration class, then yes it is normal behavior. This is you Spring java configs. So it's is Spring managed context. You are not going to call itemReader() in your code explicitly.
So, when you are going to do this:
#Autowired
private ItemReader<Measurement> iterReader;
you will get instance of your RestMeasureReader from Spring's IoC.
But if you will try to do explicitly call new RestMesureReader() inside of your code, you will get a new instance of RestMesureReader not a Spring Proxy with injected #Autowired fields.
Try to remove #Bean from your itemReader() method declaration and won't event be able to autowire RestMesureReader.
So basically #Configuration classes are just a Spring configuration, not a real java code. Even though you call new Spring will still return you a proxy class.
For more information check this guide.
Spring processes beans returned by methods that are annotated with #Bean
This allows you to use autowiring or livecycle callbacks when using Java configuration.
A more minimalistic example:
#Configuration
public class MyConfiguration {
#Bean
public A a() {
return new A();
}
static class A {
#Autowired
private B b;
#PostConstruct
public void onPostConstruct() {
System.out.println("postConstruct: " + b);
}
}
#Component
static class B {
}
}
Here, even if the bean named a is created manually, Spring will inject dependencies (b) and call #PostConstruct callbacks.

Categories