Wiring a Test in Spring Boot 2.1 without enabling bean overriding - spring-boot

I have code with named beans
#Bean
#Named("heimdall-uri-supplier")
public URISupplier heimdallEndpointSupplier(CredentialsClientConfig config, EnvInfo envInfo) {
....
}
#Named("vault-uri-supplier")
#Bean
public URISupplier vaultURISupplier(EnvInfo envInfo, CredentialsClientConfig config) {
....
}
They are explicitly Named because I want specific implementations injected into different consuming classes. These classes also use #Named.
In tests, prior to Spring Boot 2.1
#Bean
#Primary
#Named("heimdall-uri-supplier")
public URISupplier heimdallEndpointSupplier(CredentialsClientConfig config, EnvInfo envInfo) {
return mock of some sort
}
#Named("vault-uri-supplier")
#Bean
#Primary
public URISupplier vaultURISupplier(EnvInfo envInfo, CredentialsClientConfig config) {
return mock of some sort
}
worked great.
Now, of course spring boot 2.1 disables overriding. I know I can reenable it, but I'd rather not in theory do so.
But my "normal" workaround (do #Bean(name="testFoo") won't work here because the #Named injector in the consuming classes will now fail.
Are there any solutions?

I managed to do it, but it was quite painful. I had to use a BeanDefinitionRegistryPostProcessor. Setting up GenericBeanDefinition is ugly as sin. It wasn't too bad here, because the mocks are no-args. If you have to base them on injected or set constructor args, it becomes quite involved indeed.
/**
* This class basically removes the existing bean definition and substitutes in the mocks.
* Normally we don't need this - we just change the bean name. However because the
* classes use an #Named qualifier, more heroic efforts are needed
*/
public static class OverridePostProcessor implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry) throws BeansException {
if (registry.isBeanNameInUse("vault-uri-supplier")) {
registry.removeBeanDefinition("vault-uri-supplier");
}
// Note: These are a bear to work with, but in theory you can build
// them from scratch based on injected beans etc. Fortunately these
// two examples just needed a no-args supplier
GenericBeanDefinition g = new GenericBeanDefinition();
g.setBeanClass(URISupplier.class);
g.setInstanceSupplier(this::vaultURISupplier);
registry.registerBeanDefinition("vault-uri-supplier", g);
if (registry.isBeanNameInUse("taskRequestValidator")) {
registry.removeBeanDefinition("taskRequestValidator");
}
g = new GenericBeanDefinition();
g.setBeanClass(TaskRequestValidator.class);
g.setInstanceSupplier(this::taskRequestValidator);
registry.registerBeanDefinition("taskRequestValidator", g);
}
URISupplier vaultURISupplier() {
return new com.opentable.credentials.client.internal.TestVaultConfiguration.MockSupplier();
}
TaskRequestValidator taskRequestValidator() {
return (getTokenRequest, servicePolicy) -> ValidationResult.OK;
}
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
/* No op */
}
}

Related

Define a custom InjectionResolver for #Inject

My goal is to add some additional logic around the System Injection Resolver (to decorate it, so to speak). Basically, I want to register a custom injection resolver (e.g. described here), but for the javax.inject.#Inject Annotation. The setup works if I create a different custom annotation, but not when using #Inject. My implementation is as follows:
Injection Resolver:
#Singleton
#Rank(Integer.MAX_VALUE)
public class InjectInjectionResolver
implements InjectionResolver<Inject> {
private final InjectionResolver<Inject> injectionResolver;
#Inject
public InjectInjectionResolver(
#Named(InjectionResolver.SYSTEM_RESOLVER_NAME) final InjectionResolver<Inject> injectionResolver) {
this.injectionResolver = injectionResolver;
}
#Override
public Object resolve(final Injectee injectee, final ServiceHandle<?> root) {
throw new RuntimeException("Why is this never called?");
}
#Override
public boolean isConstructorParameterIndicator() {
return injectionResolver.isConstructorParameterIndicator();
}
#Override
public boolean isMethodParameterIndicator() {
return injectionResolver.isMethodParameterIndicator();
}
}
And I register the resolver as follows (inside ResourceConfig):
register(new AbstractBinder() {
#Override
protected void configure() {
bind(InjectInjectionResolver.class).to(new GenericType<InjectionResolver<Inject>>() {}).in(Singleton.class);
}
});
What I tried so far:
using #Rank
using both the org.glassfish.jersey.internal.inject.AbstractBinder or org.glassfish.hk2.utilities.binding.AbstractBinder with TypeLiteral, along with implementing the org.glassfish.hk2.api.InjectionResolver or org.glassfish.jersey.internal.inject.InjectionResolver
making sure the same setup works with #CustomAnnotation (where both the constructor and the resolve methods are called)
Does anyone know how to solve this issue?
Versions used: jersey 2.27
EDIT: When I #Inject the InjectionResolver itself, it seems that hk2's org.jvnet.hk2.internal.ThreeThirtyResolver is injected for the org.glassfish.hk2.api.InjectionResolver, while my custom injection resolver is injected for org.glassfish.jersey.internal.inject.InjectionResolver. However, the custom injection resolver is not used for injection.
The following configuration works for me, thanks to #jwells131313 for bringing me back on track. I tried to use fully qualified names, else it's probably from the javax.inject.* package.
#Singleton
#Rank(Integer.MAX_VALUE)
#org.jvnet.hk2.annotations.Service
public class MyHk2InjectionResolver
implements org.glassfish.hk2.api.InjectionResolver<Inject> {
private final org.glassfish.hk2.api.InjectionResolver<Inject> injectionResolver;
#Inject
public MyHk2InjectionResolver(
#Named(org.glassfish.hk2.api.InjectionResolver.SYSTEM_RESOLVER_NAME) final org.glassfish.hk2.api.InjectionResolver<Inject> injectionResolver) {
this.injectionResolver = injectionResolver;
}
// ... required methods, could just delegate to system resolver
}
To register the resolver, we bind it to the correct type (I do it in the ResourceConfig of a jersey application):
register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
#Override
protected void configure() {
bind(MyHk2InjectionResolver.class)
.to(new org.glassfish.hk2.api.TypeLiteral<org.glassfish.hk2.api.InjectionResolver<Inject>>() {})
.in(Singleton.class);
}
});
With this setup, you can keep using the #Inject annotation and write some custom injection logic around it, possibly using the default system injection resolver as a fallback.

Programmatic Bean registration

i would like to understand what is the programmatic equivalent of a #Bean annotated bean registration
Lets say i have a class like this:
#Configuration
public class SimpleConfiguration {
#Bean
public BigDecimal aDecimal( String example ) {
return new BigDecimal( example );
}
}
here is what i think happens here:
somehow spring register this method as a factory for a bean named
"aDecimal" of type BigDecimal, with a dependency on a bean of type
String
at some point this method will be called with the right bean as
parameter and the result will be the instance associated to the
"aDecimal" bean.
If i wanted to do the same with something like this:
#Configuration
public class DynamicConfiguration {
public void registerDefinition() {
/* i know it can't be like this but i hope it's clear what i mean */
register( "aDecimal", (example) -> aDecimal( example ) );
}
public BigDecimal aDecimal( String example ) {
/* this could be any kind of complex bean creation method */
return new BigDecimal( example );
}
}
what would be the right way to achieve this result?
i already researched a bit about this, and i found for example
How do I create beans programmatically in Spring Boot?
but this kind of registration doesn't seem as powerful as the annotation, and let's spring instatiate the bean, i want the bean to be instatied by a provided method
How to programmatically create bean definition with injected properties?
and this is missing the ability to call a method with injected bean parameters.
the reason i want to do this, is that i have some configuration classes that hold a lot of the same kind of beans with different qualifiers, based on a configuration file.
now every time the configuration file expands, i need to add new beans and configurations ( many of these are spring SessionFactories and SpringIntegration flows so i need these things to be spring beans )
You need to consider to use IntegrationFlowContext:
#Autowired
private IntegrationFlowContext integrationFlowContext;
...
IntegrationFlow myFlow = f -> ...;
BeanFactoryHandler additionalBean = new BeanFactoryHandler();
IntegrationFlowRegistration flowRegistration =
this.integrationFlowContext.registration(myFlow)
.addBean(additionalBean)
.register();
It provides for you hooks to register additional beans at runtime, not only IntegrationFlow structure.
I found the way to solve my problem, it all happens in the BeanDefinition "phase" this way everything is managed by spring and works exactly the same as as a #Bean annotated method with injected parameters, it also cleanly bridge between annotated and programmatically registered beans.
here is what i did
#Configuration
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration( classes = { TestSpringDynamicConfiguration.class } )
public class TestSpringDynamicConfiguration implements BeanDefinitionRegistryPostProcessor {
#Autowired
#Qualifier( "qualified" )
private String dynamicString;
#Bean
public Integer zero() {
return 0;
}
public String zeroString( Integer aInteger ) {
return aInteger.toString();
}
#Override
public void postProcessBeanDefinitionRegistry( final BeanDefinitionRegistry registry ) throws BeansException {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setAutowireMode( GenericBeanDefinition.AUTOWIRE_CONSTRUCTOR );
beanDefinition.setScope( BeanDefinition.SCOPE_SINGLETON );
beanDefinition.setFactoryBeanName( "testSpringDynamicConfiguration" );
beanDefinition.setFactoryMethodName( "zeroString" );
registry.registerBeanDefinition( "qualified", beanDefinition );
}
#Override public void postProcessBeanFactory( final ConfigurableListableBeanFactory beanFactory ) throws BeansException { }
#Test
public void testDynamicConfiguration() throws Exception {
assertThat( dynamicString, is( "0" ) );
}
}

How to configure springboot to wrap DataSource during integration tests?

My goal is to have a have integration tests that ensures that there isn't too many database queries happening during lookups. (This helps us catch n+1 queries due to incorrect JPA configuration)
I know that the database connection is correct because there is no configuration problems during the test run whenever MyDataSourceWrapperConfiguration is not included in the test. However, once it is added, the circular dependency happens. (see error below) I believe #Primary is necessary in order for the JPA/JDBC code to use the correct DataSource instance.
MyDataSourceWrapper is a custom class that tracks the number of queries that have happened for a given transaction, but it delegates the real database work to the DataSource passed in via constructor.
Error:
The dependencies of some of the beans in the application context form a cycle:
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
┌─────┐
| databaseQueryCounterProxyDataSource defined in me.testsupport.database.MyDataSourceWrapperConfiguration
↑ ↓
| dataSource defined in org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Tomcat
↑ ↓
| dataSourceInitializer
└─────┘
My Configuration:
#Configuration
public class MyDataSourceWrapperConfiguration {
#Primary
#Bean
DataSource databaseQueryCounterProxyDataSource(final DataSource delegate) {
return MyDataSourceWrapper(delegate);
}
}
My Test:
#ActiveProfiles({ "it" })
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration({ DatabaseConnectionConfiguration.class, DatabaseQueryCounterConfiguration.class })
#EnableAutoConfiguration
public class EngApplicationRepositoryIT {
#Rule
public MyDatabaseQueryCounter databaseQueryCounter = new MyDatabaseQueryCounter ();
#Rule
public ErrorCollector errorCollector = new ErrorCollector();
#Autowired
MyRepository repository;
#Test
public void test() {
this.repository.loadData();
this.errorCollector.checkThat(this.databaseQueryCounter.getSelectCounts(), is(lessThan(10)));
}
}
UPDATE: This original question was for springboot 1.5. The accepted answer reflects that, however, the answer from #rajadilipkolli works for springboot 2.x
In your case you will get 2 DataSource instances which is probably not what you want. Instead use BeanPostProcessor which is the component actually designed for this. See also the Spring Reference Guide.
Create and register a BeanPostProcessor which does the wrapping.
public class DataSourceWrapper implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof DataSource) {
return new MyDataSourceWrapper((DataSource)bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
Then just register that as a #Bean instead of your MyDataSourceWrapper.
Tip: Instead of rolling your own wrapping DataSource you might be interested in datasource-proxy combined with datasource-assert which has counter etc. support already (saves you maintaining your own components).
Starting from spring boot 2.0.0.M3 using BeanPostProcessor wont work.
As a work around create your own bean like below
#Bean
public DataSource customDataSource(DataSourceProperties properties) {
log.info("Inside Proxy Creation");
final HikariDataSource dataSource = (HikariDataSource) properties
.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (properties.getName() != null) {
dataSource.setPoolName(properties.getName());
}
return ProxyDataSourceBuilder.create(dataSource).countQuery().name("MyDS")
.logSlowQueryToSysOut(1, TimeUnit.MINUTES).build();
}
Another way is to use datasource-proxy version of datasource-decorator starter
Following solution works for me using Spring Boot 2.0.6.
It uses explicit binding instead of annotation #ConfigurationProperties(prefix = "spring.datasource.hikari").
#Configuration
public class DataSourceConfig {
private final Environment env;
#Autowired
public DataSourceConfig(Environment env) {
this.env = env;
}
#Primary
#Bean
public MyDataSourceWrapper primaryDataSource(DataSourceProperties properties) {
DataSource dataSource = properties.initializeDataSourceBuilder().build();
Binder binder = Binder.get(env);
binder.bind("spring.datasource.hikari", Bindable.ofInstance(dataSource).withExistingValue(dataSource));
return new MyDataSourceWrapper(dataSource);
}
}
You can actually still use BeanPostProcessor in Spring Boot 2, but it needs to return the correct type (the actual type of the declared Bean). To do this you need to create a proxy of the correct type which redirects DataSource methods to your interceptor and all the other methods to the original bean.
For example code see the Spring Boot issue and discussion at https://github.com/spring-projects/spring-boot/issues/12592.

Spring rabbit: Intercept method calls annotated with #RabbitListener without AspectJ usage

Is there a way to intercept calls of bean methods annotated with #RabbitListener without using AspectJ.
The code is something like this
#OtherAnnotation
#RabbitListener
public void do(Message message)
I need to intercept all calls to #RabbitListener method, if the method has #OtherAnnotation annotation.
UPDATE:
I managed to make it work using Gary Russell solution.
public class CustomRabbitListenerAnnotationBeanPostProcessor extends RabbitListenerAnnotationBeanPostProcessor {
#Override
protected void processAmqpListener(RabbitListener rabbitListener, final Method method, Object bean, String beanName) {
if (method.isAnnotationPresent(OtherAnnotation.class)) {
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.addAdvisor(new StaticMethodMatcherPointcutAdvisor(new OtherAnnotationInterceptor()) {
#Override
public boolean matches(Method advisorMethod, Class<?> targetClass) {
return advisorMethod.equals(method);
}
});
Object proxiedBean = proxyFactory.getProxy();
super.processAmqpListener(rabbitListener, method, proxiedBean, beanName);
} else {
super.processAmqpListener(rabbitListener, method, bean, beanName);
}
}
}
The bean definition is like:
#Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public CustomRabbitListenerAnnotationBeanPostProcessor customRabbitListenerAnnotationBeanPostProcessor() {
return new CustomRabbitListenerAnnotationBeanPostProcessor();
}
It's a bit ugly, but it works. If anyone has better solution, please share it.
You can subclass RabbitListenerAnnotationBeanPostProcessor and override the processListener method and modify the bean before invoking the super version.
Then, replace the RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME bean registered by the #EnableRabbit with your subclass.
Or, simply add your advice to the container factory's advice chain and all listeners will be advised. You can then do a runtime check to see if the other annotation is present.
i think what you are looking for is a bean-post-processor
heres a simple example:
https://www.tutorialspoint.com/spring/spring_bean_post_processors.htm
if you need to intercept calls you can wrap a proxy over the returned instance. a good example is the org.springframework.validation.beanvalidation.MethodValidationPostProcessor. you can probably extend org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor as well.
EDIT i am just learning this myself so i hope that is the right way to do this but this worked for me when experimenting with it
#Component
public class MyInterceptAnnotationBeanPostProcessor
extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
public void afterPropertiesSet() {
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(
null,
MyIntercept.class);
this.advisor = new DefaultPointcutAdvisor(pointcut, this.createAdvice());
}
protected Advice createAdvice() {
return new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("advice");
return arg0.proceed();
}
};
}
}

Custom spring property source does not resolve placeholders in #Value

I'm trying to build a Spring 3.1 PropertySource which reads its values from Zookeeper nodes. For connecting to Zookeeper I am using Curator from Netflix.
For that I've built a custom property source which reads the value of a property from Zookeeper and returns it. This works fine when I am resolving the property like this
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
However, when I try to instantiate a bean which has a field with an #Value annotation then this fails:
#Component
public class MyBean {
#Value("${foo}") public String foo;
}
MyBean b = ctx.getBean(MyBean.class); // fails with BeanCreationException
This problem has most likely nothing to do with Zookeeper but with the way I'm registering the property sources and creating the beans.
Any insight is highly appreciated.
Update 1:
I'm creating the app context from an XML file like this:
public class Main {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook();
}
}
The class which connects to Zookeeper is a #Component.
#Component
public class Server {
CuratorFramework zkClient;
public void connectToZookeeper() {
zkClient = ... (curator magic) ...
}
public void registerPropertySource() {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
}
#PostConstruct
public void start() {
connectToZookeeper();
registerPropertySource();
MyBean b = ctx.getBean(MyBean.class);
}
}
Update 2
This seems to work when I'm using XML-less configuration, i.e. #Configuration, #ComponentScan and #PropertySource in combination with an AnnotationConfigApplicationContext. Why doesn't it work with a ClassPathXmlApplicationContext?
#Configuration
#ComponentScan("com.goleft")
#PropertySource({"classpath:config.properties","classpath:version.properties"})
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Answering to your Update 2: This does not work with your original configuration(registering a PropertySource using #PostConstruct) because the PropertySource is being registered very late, by this time your target bean has already been constructed and initialized.
Typically the injection of the placeholders happens via a BeanFactoryPostProcessor which is very early in the Spring lifecycle(beans have not been created at this stage) and if a PropertySource is registered at that stage, then placeholders should be resolved.
The best approach though is to use a ApplicationContextInitializer, get a handle on the applicationContext and to register the propertySource there:
public class CustomInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
public void initialize(ConfigurableWebApplicationContext ctx) {
ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addFirst(zkPropertySource);
}
}

Resources