Overriding the InfoEndpoint in Spring Cloud - spring-boot

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.

Related

Spring Batch, Boot, JNDI and still conflicts with the transactionManager bean name

I have configured Spring Batch, Boot (2.1.4) to retrieve the transaction manager from JBoss via JNDI and I still have conflicts with the transactionManager bean name, is it possible that every time it is always the same story but for different reasons?
Now the message is:
DEBUG [org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter] (ServerService Thread Pool -- 76) Application failed to start due to an exception: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'transactionManager' defined in class path resource [org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration; factoryMethodName=transactionManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/batch/core/configuration/annotation/SimpleBatchConfiguration.class]] for bean 'transactionManager': 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.transaction.jta.JndiJtaConfiguration; factoryMethodName=transactionManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/transaction/jta/JndiJtaConfiguration.class]] bound.
Spring batch doesn't work with a transactional manager retrieved via JNDI?
Thanks
You need to give your transaction manager a name other than transactionManager because #EnableBatchProcessing already exposes a bean with that name (this is mentioned in the Javadoc of the annotation).

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.

Registering test bean with same name in Spring Framework 5.1 [duplicate]

This question already has answers here:
SpringBoot - BeanDefinitionOverrideException: Invalid bean definition
(8 answers)
Closed 3 years ago.
I'm having following config in my production files:
#Configuration
internal class Config {
#Bean
fun clock() = Clock.systemUTC()
}
In tests:
#Configuration
class ClockTestConfiguration {
#Bean
fun clock() = SetableClock()
}
My test annotations:
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = [
MyApplication::class,
ClockTestConfiguration::class
]
)
class MyTest {
...
When I was using Spring Boot 2.0.5.RELEASE it worked like a charm. After upgrading to 2.1.0.RELEASE it fails during bean registration.
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'clock' defined in com.foo.clock.ClockTestConfiguration:
Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=clockTestConfiguration; factoryMethodName=clock; initMethodName=null; destroyMethodName=(inferred);
defined in com.foo.clock.ClockTestConfiguration] for bean 'clock': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=config; factoryMethodName=clock; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/foo/clock/Config.class]] bound.
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:894)
Is there a clean way to override such bean?
You could use the properties attribute of #SpringBootTest to set spring.main.allow-bean-definition-overriding=true.

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

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

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"

Resources