how to override custom auto-configurations? - java

I have made a auto-configuration class for my project to connect to AWS Sqs. This class is working fine but when I try to override auto-configuration functionality, I am getting autowire error from calling code. Please guide if my implementation of auto-configuration class is right?
I tried different #Conditional annotations to find the solution but its not working out.
#Configuration
#ConditionalOnClass(AwsSqsAsyncClient.class)
public class AwsSqsAsyncAutoConfiguration {
#Configuration
#ConditionalOnProperty(name = "aws.sqs.queue-type", havingValue = "fifo")
#EnableConfigurationProperties(AwsSqsProperties.class)
static class AwsFifoSqsAsyncAutoConfigurationBuilder{
private final AwsSqsProperties awsSqsProperties; //another class defined by me for properties
#Inject
public AwsFifoSqsAsyncAutoConfigurationBuilder(AwsSqsProperties awsSqsProperties) {
this.awsSqsProperties = awsSqsProperties;
}
#Bean
#ConditionalOnMissingBean
public AwsSqsAsyncClient fifoAsyncClient() {
return AwsSqsFactory.createAwsSqsAsyncClient(AwsSqsMessageRequestFactory
.createAwsSqsFifoRequestFactory("producer-application-name", awsSqsProperties.getQueueUrl()),
awsSqsProperties.getAccessKey(), awsSqsProperties.getSecretKey());
}
}
}
This is where I am trying to override auto-configuration functionality
#Configuration
public class AmazonSQSConfig {
#Bean
public AwsSqsAsyncClient amazonSqsAsyncClient(){
return AwsSqsFactory
.createAwsSqsAsyncClient(AwsSqsMessageRequestFactory
.createAwsSqsFifoRequestFactory("someother-producer-application-name",
"some amazon url"), "access key",
"secret key");
}
}
calling code, this is where I am trying to autowire and getting error
"Could not autowire. there is more than one bean of type AwsSqsAsyncClient
Beans: amazonSqsAsyncClient
and fifoAsyncClient "
private final AwsSqsAsyncClient awsSqsClient;
#Autowired
public SQSPublisherImpl(AwsSqsAsyncClient awsSqsClient) {
this.awsSqsClient = awsSqsClient;
}
Spring boot factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.aws.starter.sqs.AwsSqsAsyncAutoConfiguration
I expect autoconfiguration to get disabled when I define my bean for AwsSqsAsyncClient in calling code

Related

Spring Boot: substitute #Configuration class with another

I have a custom configuration class that I am loading using spring factories during bootstrap. The problem is that it is being overwritten by another similar configuration class coming from a spring ** starter package. I've tried excluding the second one, but it still loads. Also tried to set priorities, but that didn't work too.
Here's a snippet of my custom configuration class:
#Slf4j
#Configuration
#RequiredArgsConstructor
public class CustomAwsParamStorePropertySourceLocatorConfig implements PropertySourceLocator
...
And the one I'm trying to exclude that is coming from spring boot aws starter:
public class AwsParamStorePropertySourceLocator implements PropertySourceLocator {
The AwsParamStoreBootstrapConfiguration class has the ConditionalOnProperty annotation at the class level...
#Configuration(proxyBeanMethods = false)
#EnableConfigurationProperties(AwsParamStoreProperties.class)
#ConditionalOnClass({ AWSSimpleSystemsManagement.class, AwsParamStorePropertySourceLocator.class })
#ConditionalOnProperty(prefix = AwsParamStoreProperties.CONFIG_PREFIX, name = "enabled", matchIfMissing = true)
public class AwsParamStoreBootstrapConfiguration {
private final Environment environment;
public AwsParamStoreBootstrapConfiguration(Environment environment) {
this.environment = environment;
}
#Bean
AwsParamStorePropertySourceLocator awsParamStorePropertySourceLocator(AWSSimpleSystemsManagement ssmClient,
AwsParamStoreProperties properties) {
if (StringUtils.isNullOrEmpty(properties.getName())) {
properties.setName(this.environment.getProperty("spring.application.name"));
}
return new AwsParamStorePropertySourceLocator(ssmClient, properties);
}
So if you configured the property aws.paramstore.enabled=false it should stop that configuration from creating the AwsParamStorePropertySourceLocator bean.
It's important to note, that would also stop the creation of the AWSSimpleSystemsManagement bean which is also created in the AwsParamStoreBootstrapConfiguration class, so if you require that bean, you may need to also create it in your custom Configuration class.
#Bean
#ConditionalOnMissingBean
AWSSimpleSystemsManagement ssmClient(AwsParamStoreProperties properties) {
return createSimpleSystemManagementClient(properties);
}
public static AWSSimpleSystemsManagement createSimpleSystemManagementClient(AwsParamStoreProperties properties) {
AWSSimpleSystemsManagementClientBuilder builder = AWSSimpleSystemsManagementClientBuilder.standard()
.withClientConfiguration(SpringCloudClientConfiguration.getClientConfiguration());
if (!StringUtils.isNullOrEmpty(properties.getRegion())) {
builder.withRegion(properties.getRegion());
}
if (properties.getEndpoint() != null) {
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
properties.getEndpoint().toString(), null);
builder.withEndpointConfiguration(endpointConfiguration);
}
return builder.build();
}

Spring Junit Configuration class not loaded

I'm writing a junit test where I need to Autowire a specific implementation of an interface. I'm using the #Mock annotation to Autowire the implementation.
I'm using profiles and a Configuration file to determine which implementation to Autowire.
When running the test class EmailTest , the following error message appears on the console:
Caused by: java.lang.IllegalStateException: Unable to register mock bean .... expected a single matching bean to replace but found [customerEmailSender, emailSenderImpl_1, emailSenderImpl_2]
The reason is that the Spring doesn't find or use the config class : BeanConfiguration.
I know this because I put a breakpoint in the class BeanConfiguration, and the application doesn't break.
What could be the raeson that Spring doesn't find or use the configuration class BeanConfiguration.
#RunWith(SpringRunner.class)
#ActiveProfiles(profiles = {"test-unit"})
#Import(BeanConfiguration.class)
public class EmailTest {
#MockBean
private CustomerEmailSender customerEmailSender;
}
#Configuration
public class BeanConfiguration {
#Profile({"test-unit"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_1(){
return new EmailSenderImpl_1();
}
#Profile({"prd"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_2(){
return new EmailSenderImpl_2();
}
}
Check if BeanConfiguration package is till in spring's component scan boundary
The problem is that #MockBean does not know which bean to replace (and you have multiple beans of the same interface).
Try
#MockBean(name = "customerEmailSender")
private CustomerEmailSender customerEmailSender;

SpringBoot - Register Bean as Rest Controller

I'm using Spring Boot AutoConfiguration for registering the beans. Need to find a way through which I can register the beans configured via Auto-Configuration as a Rest Controller
SampleController.java
public class SampleController
{
#GetMapping("/sample-path")
public String sampleMethod()
{
return "Sample String";
}
}
SampleControllerAutoConfiguration.java
#Configuration
#ConditionalOnProperty(value = "some.property", havingValue = "true") // using this property, the parent app may or may not chose to have the Controller Endpoint
public class SampleControllerAutoConfiguration
{
// Need to register this bean as a controller
#Bean
#ConditionalOnMissingBean
public SampleController sampleController()
{
return new SampleController();
}
}
I can't annotate SampleController with #RestController since it's in the same package as the parent project which imports this and hence get's auto-configured due to Component-Scan

Can't achieve dependency injection oustide a controller in Spring Booot

I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.

Using different java based spring context configurations with cucumber

I am trying to get Cucumber working with Spring. In our code, we are already using java based Spring configuration. I am having trouble getting it to work in the following scenario. Can someone please help?
Today , in our integration test classes we use #ContextConfiguration for each class and provide the config class that is declared with in that integration test class for loading the beans. Config class is annotated with #Configuration. Same bean could be instantiated differently in 2 different classes Config classes used in 2 different integration test classes.
So when I use Cucumber, since the Contextconfiguration differs on different classes, it looks for 'Cucumber.xml' . In the xml file, I am using component-scan to scan the cucumber step definition classes by giving the package name that these classes use (both classes have same package name) . Since all beans gets loaded in same context, Cucumber is failing to load the beans when it finds the same bean defined in these different config classes .
How do I get over this problem of creating same bean but in different ways and use them in different classes?
Please note that I am not looking for a solution that creates lot of churn from our existing coding practices, so having per-test-xml file is not an option for me.
Here is how our code looks:
Class NameAndAddressProviderIntegrationTestSteps :-
#ContextConfiguration(locations="classpath:cucumber.xml")
public class NameAndAddressProviderIntegrationTestSteps {
#Configuration
#Import({
xyz.class,
abc.class,
NameAndAddressProvider.class
})
#ImportResource({
"file:configuration/spring-configuration/abc.xml",
"file:configuration/spring-configuration/xyz.xml"
})
public static class Config {
#Bean
AccountHolderDataMap dataMap() {
AccountHolderDataMap data = new AccountHolderDataMap();
data.put(ID,
new AccountHolderData(customerID));
data.get(customerID).setCustomerplaceID(testCustomerplaceID);
return data;
}
}
#Inject
private NameAndAddressProvider provider;
#When("^I call nameandAddress provider with a 'customerId'$")
public void i_call_nameandAddress_provider_with_a_customerId() throws DependencyException {
System.out.println("Entering when method");
names = provider.getNames(customerID);
System.out.println(provider.toString());
}
......
}
Class AddressProviderIntegrationTestSteps:-
#ContextConfiguration(locations="classpath:cucumber.xml")
public class AddressProviderIntegrationTestSteps {
#Configuration
#Import({
abc.class,
xyz.class,
AddressesProvider.class
})
#ImportResource({
"file:configuration/spring-configuration/test-environment.xml",
"file:configuration/spring-configuration/test-logging-config.xml"
})
public static class Config {
#Bean
#DependsOn("Environment")
AccountHolderDataMap data() {
AccountHolderDataMap data = new AccountHolderDataMap();
data.put(testCustomerID,
new AccountHolderData(testCustomerID, testCustomerplaceID,businessType));
return data;
}
}
private static final String testCustomerID = "1234";
private static final String testMarketplaceID = "abc";
#Inject
private AddressesProvider provider;
#When("^I call AddressesProvider provider with a 'CustomerID'$")
public void i_call_AddressesProvider_provider_with_a_CustomerID() throws Throwable {
List<Address> addresses = provider.getAddresses(testCustomerID);
Log.info(addresses.get(0).toString());
assertTrue(addresses.size()==1);
}
}
And here is the nested exception I am getting:-
"nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [....AccountHolderDataMap] is defined: expected single matching bean but found 2: dataMap,data"
Appreciate your help!
I've managed multiple sources for bean-definitions. You can use this at a starting point (or others in the internet as your question is quite old)
I am using spring4, see my other cucumer post for the pom
At the stepdefs use a config.class
#ContextConfiguration(classes = { CucumberConfiguration.class })
public class StepdefsTest123 {
#Autowired bean; // from cucumberBeanContext.xml
#When("^A$")
public void a() throws Throwable {
System.out.println(bean.getFoo());
}
}
in the config-class add aditional beandefinitions
#Configuration
#ComponentScan(basePackages = "package.here.cucumber")
#ImportResource("classpath:cucumberBeanContext.xml")
public class CucumberConfiguration {
// nothing to do here
}

Categories

Resources