How to set spring.main.allow-bean-definition-overriding to true in a Spring boot 2.1.0 starter configuration - spring-boot

I maintain a spring-boot-starter that customizes the error attributes returned when, for instance, a unknown end point is called.
This is done by overriding the org.springframework.boot.web.servlet.error.ErrorAttributes bean.
Everything worked fine with 2.0.6, but 2.1.0 disables bean overriding by default, making the starter now fail with the following message.
Invalid bean definition with name 'errorAttributes' defined in class
path resource
[com/mycompany/springboot/starter/config/ErrorsConfig.class]: Cannot
register bean definition [Root bean: class [null]; scope=;
abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0;
autowireCandidate=true; primary=false;
factoryBeanName=com.mycompany.springboot.starter.config.ErrorsConfig;
factoryMethodName=errorAttributes; initMethodName=null;
destroyMethodName=(inferred); defined in class path resource
[com/mycompany/springboot/starter/config/ErrorsConfig.class]] for bean
'errorAttributes': There is already [Root bean: class [null]; scope=;
abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0;
autowireCandidate=true; primary=false;
factoryBeanName=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
factoryMethodName=errorAttributes; initMethodName=null;
destroyMethodName=(inferred); defined in class path resource
[org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class]]
bound
As explained in documentation setting the spring.main.allow-bean-definition-overriding property to true fixes the problem.
My question is how to do that in the starter (I do not want the users of my starter to have to change their application.properties file, for something that is specific to my starter)?
I tried to a #PropertySource("classpath:/com/mycompany/starter/application.properties") annotation to my #Configuration with that property defined in that file, but it doesn't work.
What am I missing? Is there any way to allow my configuration overriding that bean?
Here is the (simplified) source code of the configuration:
#Configuration
#PropertySource("classpath:/com/mycompany/starter/application.properties")
public class ErrorsConfig {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#SuppressWarnings("unchecked")
#Override
public Map<String, Object> getErrorAttributes(WebRequest request, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(request, includeStackTrace);
// CustomeError is a (simplified) bean of the error attributes we should return.
CustomError err = new CustomError("myErrorCode", (String) errorAttributes.get("error"));
return OBJECT_MAPPER.convertValue(err, Map.class);
}
};
}
}
and my resource file com/mycompany/starter/application.properties contains
spring.main.allow-bean-definition-overriding=true

Spring Boot's ErrorAttributes bean is defined by ErrorMvcAutoConfiguration. It is annotated with #ConditionalOnMissingBean so it will back off if an ErrorAttributes bean has already been defined. As the bean defined by your ErrorsConfig class is attempting to override Boot's ErrorAttributes bean rather than causing it to back off, your ErrorsConfig class must be getting processed after Boot's ErrorMvcAutoConfiguration class. This means that you have an ordering problem in your starter.
The order in which auto-configuration classes are processed can be controlled using #AutoConfigureBefore and #AutoConfigureAfter. Assuming that ErrorsConfig is itself an auto-configuration class registered in spring.factories, you can fix your problem by annotating it with #AutoConfigureBefore(ErrorMvcAutoConfiguration.class). With this change in place ErrorsConfig will define its ErrorAttributes bean before ErrorMvcAutoConfiguration attempts to do so which will cause the auto-configuration of Boot's ErrorsAttribute bean to back off.

Easier solution would be to add this property spring.main.allow-bean-definition-overriding=true in the application.properties.
Reference

I have also faced similar issue. But the problem in my case is that, I was using #Entity annotaion in my model class. I was using mongodb for the backend so entity was from Jpa and hence resulted in this issue. Once I removed that it started working

Related

how to disable autoconfiguration in integration test?

I am trying this:
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
)
#EnableAutoConfiguration(
exclude = [
ReactiveOAuth2ClientAutoConfiguration::class,
ReactiveOAuth2ResourceServerAutoConfiguration::class,
]
)
#ActiveProfiles("testcontainers")
internal class RecordIntegrationTest {
#Test
fun contextLoads() {}
}
With testcontainers profile in application.yml:
spring:
config.activate.on-profile: testcontainers
r2dbc.url: r2dbc:tc:postgresql:///databasename?TC_IMAGE_TAG=13.2
flyway:
enabled: false
But then it tries to create repositories twice (and potentially other beans as well):
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'recordRepository' defined in com.example.RecordRepository defined in #EnableR2dbcRepositories declared on R2dbcRepositoriesAutoConfigureRegistrar.EnableR2dbcRepositoriesConfiguration: Cannot register bean definition [Root bean: class [org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in com.example.RecordRepository defined in #EnableR2dbcRepositories declared on R2dbcRepositoriesAutoConfigureRegistrar.EnableR2dbcRepositoriesConfiguration] for bean 'recordRepository': There is already [Root bean: class [org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in com.example.RecordRepository defined in #EnableR2dbcRepositories declared on R2dbcRepositoriesAutoConfigureRegistrar.EnableR2dbcRepositoriesConfiguration] bound.
UPDATE
Found out that the test runs fine when I move it to the root package
src/test/kotlin/com/example/backend
But it fails when I put it in the package
`src/test/kotlin/com/example/backend/subfolder`
UPDATE 2
I figured out that if a repository defined under src/main/kotlin is in the same package as the integration test that exception shows up.
E.g.:
data class X(#Id id: Long? = null)
// src/main/kotlin/com/example/x/XRepository.kt
interface XRepository : CoroutineCrudRepository<X, Long> {}
This test fails with the above exception:
// src/test/kotlin/com/example/x/XIntegrationTest.kt
#SpringBootTest(classes = [BackendApplication::class])
#EnableAutoConfiguration(
exclude = [
ReactiveOAuth2ClientAutoConfiguration::class,
ReactiveOAuth2ResourceServerAutoConfiguration::class,
]
)
#ActiveProfiles("testcontainers")
internal class XIntegrationTest {
#Test
fun contextLoads() {
}
}
Deleting the repository or moving the integration test to a different package works fine.
Include DataSourceAutoConfiguration in exclude block used by EnableR2dbcRepositories which disables the checking of datasource while starting up your integration test which is used by Repositories or you can configure the datasource to fix this issue.
The issue is basically due to the unavailability of datasource required by the repositories
Somewhere in your src/main/java you have a class that brings the annotation #EnableAutoConfiguration. This is the class that you want to exclude from your tests, when you have defined your own #EnableAutoConfiguration in some other class in test package.

Not able to execute DataJpaTest

I have a spring-data-jpa repository called TagRepository. My spring-boot version is 2.1.2. I am using H2 dependency with runtime scope and I intend to use it for both the application and integration testing. I want to write a DataJpaTest for the TagRepository. I have written the following code:
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
#DataJpaTest
#ContextConfiguration(classes={TagRepository.class})
public class TagRepositoryTest {
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private TagRepository tagRepository;
#Test
public void findByTagTest() {
Tag tag = new Tag("java");
testEntityManager.persistAndFlush(tag);
Optional<Tag> optionalTag = tagRepository.findByTag(tag.getTag());
if(!optionalTag.isPresent()) {
fail("Tag not found hence test failed");
}
assertThat(optionalTag.get()).isEqualTo(tag);
}
}
However, when I execute the test it says Application failed to start and I get following error:
Invalid bean definition with name 'tagRepository' defined in null: Cannot register bean definition [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'tagRepository': There is already [Generic bean: class [com.upday.task.repository.TagRepository]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
The bean 'tagRepository', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
The test class itself resides in a different package than jpa repository.
Just remove
#ContextConfiguration(classes={TagRepository.class})
from your test.
It is meant for passing a custom #Configuration not a regular bean.
UPD:
When data jpa test and the repository under test resides in a different packages spring runner needed a little hint to scan classes from another place. Pointing out the application class via
#ContextConfiguration(classes={SpringBootApplication.class})
will extend the component scan scope and allow test environment to pick up additional beans.

Spring OAUTH: Override CheckTokenEndpoint 'check_token?token=' response map

I would like to override the CheckTokenEndpoint to provide my own custom output as Map to the resource server. I have tried the following, but not working.
Introducing new custom controller for (/oauth/check_token), but Spring rejects this custom and registers its own.
Overriding bean definition for bean 'checkTokenEndpoint' with a
different definition: replacing [Generic bean: class
[com.datami.auth.security.CheckTokenEndpoint]; scope=singleton;
abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0;
autowireCandidate=true; primary=false; factoryBeanName=null;
factoryMethodName=null; initMethodName=null; destroyMethodName=null;
defined in file
[/usr/local/Cellar/tomcat/8.5.5/libexec/webapps/oauth-server/WEB-INF/classes/com/datami/auth/security/CheckTokenEndpoint.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false;
autowireMode=3; dependencyCheck=0; autowireCandidate=true;
primary=false;
factoryBeanName=org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
factoryMethodName=checkTokenEndpoint; initMethodName=null;
destroyMethodName=(inferred); defined in class path resource
[org/springframework/security/oauth2/config/annotation/web/configuration/AuthorizationServerEndpointsConfiguration.class]]
Created my own endpoint with (/oauth/check_custom_token) but not sure autowiring resourceServerTokenServices in the below, #autowire doesn't helped me.
#autowire
private ResourceServerTokenServices resourceServerTokenServices;
Spring has autowired this with DefaultTokenServices.
I can also create new DefaultTokenServices() in my code, but then how to autowire the below inside DefaultTokenServices? again the same problem.
private TokenStore tokenStore;
private ClientDetailsService clientDetailsService;
private TokenEnhancer accessTokenEnhancer;
private AuthenticationManager authenticationManager;
Coul you please help me out.
CheckTokenEndpoint depends on its accessTokenConverter instance to create and return the map.
You could create a custom AccessTokenConverter (maybe extending from OOTB DefaultAccessTokenConverter if needed) and use it like so:
#Configuration
#EnableAuthorizationServer
public class MyAuthConfig extends AuthorizationServerConfigurerAdapter {
...
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.accessTokenConverter(new MyAccessTokenConverter())...
....
Of course, you might want to use a factory method to create your accessTokenConverter instance, which allows you to inject a few properties into the instance etc.
Once done, inside AuthorizationServerEndpointsConfiguration.checkTokenEndpoint you can see that the accessTokenConverter you set above will be passed to the OOTB instance of CheckTokenEndpoint and used to create the map.

Spring - custom userDetailsService

I am having issues in using Spring boot with Spring Activiti. we have userDetailsService defined and its working fine with out adding activiti dependency. But with activiti my userDetailsService is getting overridden with root userDetailsService
Overriding bean definition for bean '**userDetailsService**' with a different definition: replacing [Generic bean: class [com.partshub.security.UserDetailsService]; .....l; defined in file [C:\GFApps\spring-workspace\Spring-SPA\target\classes\com\partshub\security\UserDetailsService.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.activiti.spring.boot.SecurityAutoConfiguration$UserDetailsServiceConfiguration; factoryMethodName=userDetailsService; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/activiti/spring/boot/SecurityAutoConfiguration$UserDetailsServiceConfiguration.class]]
Maven dependency added
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>5.21.0</version>
</dependency>
Appreciate your help
adding below property entry fixed my issue
spring.autoconfigure.exclude = "org.activiti.spring.boot.RestApiAutoConfiguration, org.activiti.spring.boot.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration"

Overriding the InfoEndpoint in Spring Cloud

Originally posted this as an issue in GitHub but might be better suited for this forum...
We are assigning some custom metadata values to our Spring Cloud services that are being registered in Eureka and we now need to have visibility to those values in the Eureka dashboard. I am trying to extend/override the /info endpoint so that our metadata is visible from the Eureka Dashboard which already provides hyperlinks to each registered service's /info endpoint. I had read that I could override the Boot autoconfigured InfoEndpoint by just adding my own version of that bean to the context. I am trying to test with the following configuration bean:
#Configuration
public class EndpointConfig {
#Bean
public InfoEndpoint infoEndpoint() throws Exception {
LinkedHashMap<String, Object> info = new LinkedHashMap<String, Object>();
info.put("name", "value");
return new InfoEndpoint(info);
}
}
When I run my service though and hit its /info endpoint I do not see this test value. I also see this in the log:
2015-04-17 14:54:23,910 main INFO DefaultListableBeanFactory - - - -
Overriding bean definition for bean 'infoEndpoint': replacing [Root
bean: class [null]; scope=; abstract=false; lazyInit=false;
autowireMode=3; dependencyCheck=0; autowireCandidate=true;
primary=false; factoryBeanName=endpointConfig;
factoryMethodName=infoEndpoint; initMethodName=null;
destroyMethodName=(inferred); defined in class path resource
[com/acme/ecom/items/config/EndpointConfig.class]] with [Root bean:
class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3;
dependencyCheck=0; autowireCandidate=true; primary=false;
factoryBeanName=org.springframework.cloud.autoconfigure.RefreshAutoConfiguration$InfoEndpointRebinderConfiguration;
factoryMethodName=infoEndpoint; initMethodName=null;
destroyMethodName=(inferred); defined in class path resource
[org/springframework/cloud/autoconfigure/RefreshAutoConfiguration$InfoEndpointRebinderConfiguration.class]]
It appears that my version of the infoEndpoint bean is being replaced with another bean coming from Spring cloud (in org/springframework/cloud/autoconfigure/RefreshAutoConfiguration$InfoEndpointRebinderConfiguration.class).
Am I reading this correctly? And if so how do I prevent it?
Thanks,
Bill
If you put your metadata in info.* e.g. info.myfoo=${eureka.instance.metadataMap.myfoo:none} it will show up in the default /info endpoint.
Don't know if this will actually help but I have this in my application.yml which I can see when I do /info on the service.
info:
component: Service Name
so when i hit the service with /info this is what i see:-
{
"component": "Service Name"
}
Hope this helps.

Resources