Conditional creating a bean based on customized biz logic - spring

Here're my use case:
When my app launched, N beans of type A will be created.
I'd like to have my own biz logic to check these N beans 1 by 1 and:
if none of them satisfy my criteria, I'll create another bean of type A to spring container.
if any of them satisfy my criteria, just do nothing.
I'm not sure whether I can simply use Optional like this:
#Autowired
List<A> beans;
#Bean
public Optional<A> maybeA(){
//check beans and optionally create a A
}

Defining bean that returns type Optional is very unusual and not a good solution.I suggest you check if you could implement your logic as described https://iamninad.com/conditional-bean-creation-in-spring-boot/ or if you insist to follow your solution, you can create a new class as :
public class UnMatchedACriteriaImpl implements A {
public void testMethod() throws UnSatisfiedXCriteria {
throw new UnSatisfiedXCriteria();
}
}
and change your bean definition to :
#Bean
public A maybeA(){
//check beans and optionally create a A
}
When you criteria does not match you return UnMatchedACriteriaImpl version of A. which is clear and manageable when you want to use it in your application.

Related

Replacing micronaut's default KafkaProducerFactory with custom factory implementation

I need to customize the default KafkaProducerFactory (or any other default factory, say KafkaConsumerFactory) that ships with micronaut-kafka dependency. For that I tried to replace the existing factory using,
#Factory
#Replaces(factory = KafkaProducerFactory.class)
class CustomFactory extends KafkaProducerFactory {
#Bean
#Any
public <K, V> Producer<K, V> getProducer(
#Nullable InjectionPoint<KafkaProducer<K, V>> injectionPoint,
#Nullable #Parameter AbstractKafkaProducerConfiguration<K, V> producerConfiguration) {
validate(producerConfiguration); //this is my primary intension
super.getProducer(injectionPoint, producerConfiguration);
}
}
But it seems that Micronaut is not able to replace KafkaProducerFactory hence both the factory exists and I am getting error saying
"multiple candidate bean exists [CustomFactory, KafkaProducerFactory]"
I also thought to exclude the KafkaProducerFactory while the application loads, but could not find anything similar to Spring's ComponentScan.excludeFilter in Micronaut.
Is there anything wrong in my configuration or is there any other way to achieve the same?
Finally I got the answer. Let me elaborate little more for the actual context,
Problem
We have custom way of creating producer/consumer instances i.e. a custom class that creates those given the config properties. Now I had to modify the default factories so that instead of creating the instances on its own, the factory should invoke our custom class to instantiate producer/consumers.
Solution
I had to add #Primary along with other annotations and its working,
#Factory
#Replaces(factory = KafkaProducerFactory.class)
#Primary
class CustomFactory extends KafkaProducerFactory {
//code here
}
But the way I acheived this is kind of a work around because,
my primary intension was to override the producer/consumer creation part of default factory in a sub class and then replacing the default factory by the sub class. But as per the code structure in default factory class, it was not a single place where we can plug our custom code (no specific public method present consolidating the code for creating producer/consumer, it was being created from 3 separate places with "new") to create the producer. Hence we had to copy the entire default factory class and replaced the 3 places with custom code which does not seem to be a correct way.

How to choose bean implementation at runtime for every http request

I am having two implementations of my component.
public interface MyComponent {
}
imple1
#Component("impCompf")
#Lazy
#RequestScope
public class ImpComp1 implements MyComponent {
}
imple2
#Component("impComps")
#Lazy
#RequestScope
public class ImpComp2 implements MyComponent {
}
What I did so far is to create two conditions like so:
imple1
public class FirstCondition implements Condition {
#Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return staticVariable.contains("impCompf");
}
}
Same goes for imple2
and define a configuration class
#Configuration
public class MyConfiguration {
#Bean
#Conditional(FirstCondition .class)
#Primary
public MyComponent getComp1() {
return new ImpComp1();
}
public static String staticVariable= "impCompf";
and in My main controller:
#RequestMapping(value="api/{co}", method=RequestMethod.POST)
public ResponseEntity<Modelx> postSe(#PathVariable("co") String co) {
if(co.contains("impCompf"))
staticVariable = "impCompf";
else (co.contains("impComps"))
staticVariable = "impComps";
What I want: for every http request I want to load proper implementation
But however what I am getting is the implementation defined first in the static variable.
If is there another elegant and better way, i'd like to know about it.
I think there is some confusion here about the purpose of the conditions. These aren't being used at the time your requests arrive to autowire the candidate bean into your controller. These are being used when the application is started to configure the application context based on the environment and classpath etc...
There is no need for the conditional classes that you have created. This is defining the configuration of the beans when the context starts and not on a per request basis at runtime.
The use of the static variable is also problematic is a scenario with one or more concurrent requests or in a case where multiple threads may observe different values unless some other mechanism in the java memory model is being used (such as volatile or establishing a happens before relationship, e.g. with sychnronized)
There are a number of ways to do what you appear to be trying to achieve. Since ultimately, you appear to be using a path parameter supplied by a client to determine which service you want to invoke you could use a classic factory pattern to return the correct interface implementation based on the string input programmatically.
Alternatively you could create two distinct controller methods which are distinguished by a query parameter or endpoint name or path match etc. You could then have the appropriate service injected by a qualified bean name
Although perhaps generally recommended, you could also inject an application context instance and search the it looking for the relevant bean by name or class: https://brunozambiazi.wordpress.com/2016/01/16/getting-spring-beans-programmatically/ - although This is more cumbersome and you'd need to handle things like org.springframework.beans.factory.NoSuchBeanDefinitionException or casting in some cases - best avoided in favour of one of the other methods.

spring-security global-method-security protect-pointcut with #EnableGlobalMethodSecurity

How does one port from
<sec:global-method-security secured-annotations="disabled">
<sec:protect-pointcut expression='execution(* x.y.z.end*(..))' access='...' />
to spring java-config
#EnableGlobalMethodSecurity
#Configuration
public class MyConfiguration extends WebSecurityConfigurerAdapter {
?
There is a simmilar question here http://forum.spring.io/forum/spring-projects/security/726615-protect-pointcut-in-java-configuration
There's a workaround for it. The security points information is kept in MethodSecurityMetadataSource implementations (which are then used by MethodInterceptor) so we have to create an additional MethodSecurityMetadataSource. As mentioned in the spring forum post the xml pointcut configuration is kept in MapBasedMethodSecurityMetadataSource and processed by ProtectPointcutPostProcessor. we also need an instance of ProtectPointcutPostProcessor. Unfortunately this class is final and package-private so there are 2 options:
create your own class and copy/paste the whole content of the original one (that's what I did)
change the class modifiers with reflection and create an instance of the original one (haven't done that so no idea if it would work fine)
then create the following beans in your context:
#Bean
public Map<String, List<ConfigAttribute>> protectPointcutMap() {
Map<String, List<ConfigAttribute>> map = new HashMap<>();
// all the necessary rules go here
map.put("execution(* your.package.service.*Service.*(..))", SecurityConfig.createList("ROLE_A", "ROLE_B"));
return map;
}
#Bean
public MethodSecurityMetadataSource mappedMethodSecurityMetadataSource() {
// the key is not to provide the above map here. this class will be populated later by ProtectPointcutPostProcessor
return new MapBasedMethodSecurityMetadataSource();
}
// it's either the original spring bean created with reflection or your own copy of it
#Bean
public ProtectPointcutPostProcessor pointcutProcessor() {
ProtectPointcutPostProcessor pointcutProcessor = new ProtectPointcutPostProcessor((MapBasedMethodSecurityMetadataSource) mappedMethodSecurityMetadataSource());
pointcutProcessor.setPointcutMap(protectPointcutMap());
return pointcutProcessor;
}
we've created the necessary beans, now we have to tell spring to use them. I'm assuming you're extending GlobalMethodSecurityConfiguration. by default it creates DelegatingMethodSecurityMetadataSource which contains a list of other MethodSecurityMetadataSources. Depending on what you want to achieve you have following options:
if you want to keep all the other MethodSecurityMetadataSources (like the ones for parsing the #Secured annotations) you can extend the list in the delegating metadata source by overriding the following method:
#Override
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return mappedMethodSecurityMetadataSource();
}
it would inject it on first place in the list though which may cause some problems.
if you want to keep the other sources but want yours to be the last in the list then override the following method:
#Override
public MethodSecurityMetadataSource methodSecurityMetadataSource() {
DelegatingMethodSecurityMetadataSource metadataSource = (DelegatingMethodSecurityMetadataSource) super.methodSecurityMetadataSource();
metadataSource.getMethodSecurityMetadataSources().add(mappedMethodSecurityMetadataSource());
return metadataSource;
}
if you want your source to be the only one (you don't want to use #Secured or any other annotations) then you can override the same method, just with different content
#Override
public MethodSecurityMetadataSource methodSecurityMetadataSource() {
return mappedMethodSecurityMetadataSource();
}
that's it! I hope it will help
I followed #marhewa comments and have been able to use the Spring version of class ProtectPointcutPostProcessor by defining the following bean
/**
* Needed to use reflection because I couldn't find a way to instantiate a
* ProtectPointcutPostProcessor via a BeanFactory or ApplicationContext. This bean will process
* the AspectJ pointcut defined in the map; check all beans created by Spring; store the matches
* in the MapBasedMethodSecurityMetadataSource bean so Spring can use it during its checks
*
* #return
* #throws Exception
*/
#Bean(name = "protectPointcutPostProcessor")
Object protectPointcutPostProcessor() throws Exception {
Class<?> clazz =
Class.forName("org.springframework.security.config.method.ProtectPointcutPostProcessor");
Constructor<?> declaredConstructor =
clazz.getDeclaredConstructor(MapBasedMethodSecurityMetadataSource.class);
declaredConstructor.setAccessible(true);
Object instance = declaredConstructor.newInstance(pointcutMethodMetadataSource());
Method setPointcutMap = instance.getClass().getMethod("setPointcutMap", Map.class);
setPointcutMap.setAccessible(true);
setPointcutMap.invoke(instance, pointcuts());
return instance;
}
This way I don't need to duplicate the code of this Spring class.
Cheers

Component Scan for custom annotation on Interface

I have a component scan configuration as this:
#Configuration
#ComponentScan(basePackageClasses = {ITest.class},
includeFilters = {#ComponentScan.Filter(type = FilterType.ANNOTATION, value = JdbiRepository.class)})
public class MyConfig {
}
Basically I would like to create scan interface which has JdbiRepository annotation
#JdbiRepository
public interface ITest {
Integer deleteUserSession(String id);
}
And I would like to create proxy implementation of my interfaces. For this purpose I registered a custom SmartInstantiationAwareBeanPostProcessor which is basically creating necessary instances but the configuration above does not scan interfaces which has JdbiRepository annotation.
How can I scan interfaces by custom annotation?
Edit:
It seems that org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent is accepting only concrete classes.
/**
* Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is concrete
* (i.e. not abstract and not an interface). Can be overridden in subclasses.
* #param beanDefinition the bean definition to check
* #return whether the bean definition qualifies as a candidate component
*/
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
}
Edit:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface JdbiRepository {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* #return the suggested component name, if any
*/
String value() default "";
}
Creating a Dummy implementation seems quite hacky to me and all the steps Cemo mentioned require a lot of effort. But extending ClassPathScanningCandidateComponentProvider is the fastest way:
ClassPathScanningCandidateComponentProvider scanningProvider = new ClassPathScanningCandidateComponentProvider(false) {
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return true;
}
};
Now you´re also able to scan for Interfaces with (custom) Annotations with Spring - which also works in Spring Boot fat jar environments, where the fast-classpath-scanner (which is mentioned in this so q&a) could have some limitations.
As I said earlier, component scan is working only for concrete classes.
In order to solve my problem I have followed these steps:
Implemented a custom org.springframework.context.annotation.ImportBeanDefinitionRegistrar
Created a custom annotation EnableJdbiRepositories which is importing my custom importer.
Extended ClassPathScanningCandidateComponentProvider in order to scan interfaces too. My custom importer used this class to scan interfaces too.
Instead of scanning an annotated interface, you can create a dummy implementation and annotate it accordingly:
#JdbiRepository
public class DummyTest implements ITest {
public Integer deleteUserSession(String id) {
// nothing here, just being dummy
}
}
Then, scan the dummy implementation basePackageClasses = {DummyTest.class}.
It's a bit of a workaround, but very simple and good enough for test purposes (as seems to be here).

How to Produce prototype objects from singleton? (Design help needed)

I'm relatively new to Spring and I've got myself dug in a hole. I'm trying to model motor cars. Each model has it's own builder object, and I have a BuilderFactory that returns the correct builder based upon user selection from a web-app.
So I'm looking for suggestions on how to approach this problem where I need to create a number of individual vehicles, but I don't know what type of vehicle I'm going to need until run-time, and each vehicle needs to be unique to the user.
What I've got at the moment is shown below. The problem I have at the moment is that because the individual builders are singletons so are the individual vehicles. I need them
to be prototypes. I know it all looks pretty horrible so I'm sure there must be a better way of doing this.
The top level from the web-app looks like;
Vehicle vehicle = vehicleBuilderFactory.getBuilder(platform).build();
My vehicleBuilderFactory looks like this;
#Service
public class VehicleBuilderFactory {
#Autowired
Discovery3Builder discovery3Builder;
#Autowired
Discovery4Builder discovery4Builder;
// Lots of #Autowired statements here.
#Autowired
FreeLander2010Builder freeLander2010Builder;
public VehicleBuilder getBuilder(Platform platform) {
switch (platform.getId()) {
case 1: return discovery3Builder;
case 2: return discovery4Builder;
// Lots of case statements here
case 44: return freeLander2010Builder;
default: return null;
}
}
}
which itself looks pretty horrible. Each individual builder looks like;
#Service
public class DefenderBuilder implements VehicleBuilder {
#Autowired
Defender defender;
// Loads of Defender specific setters ommitted
#Override
public Vehicle build() {
return defender;
}
}
and finally the individual vehicle
#Service
#Scope("prototype")
public class Defender extends Vehicle {
}
The main problem now, is that because the builders are singletons, so are the vehicles, and
I need them to be prototypes, because User A's Defender is different to user B's Defender.
You can use Spring's ObjectFactory to have it service up prototype scoped beans from a singleton scoped bean. The usage is pretty straightforward:
#Component
class DefenderBuilder implement VechicleBuilder {
#Autowired
ObjectFactory<Defender> defenderFactory;
Defender build() {
return defenderFactory.getObject()
}
}
#Component
#Scope("prototype")
class Defender {
}
This returns a new Defender on each call to defenderFactory.getObject()
Without reading too much into the detail you say you want to produce Prototype beans from a singleton possibly with a look up in the IoC container.
Section 3.4.6.1 Lookup method injection of the Spring documentation describes how this can be done without losing the Inversion of Control i.e. without your beans knowing about the bean store.
I have made use of the ServiceLocatorFactoryBean to solve a similar problem before. The class level Javadoc is excellent and contains some clear examples.
Two things:
1) You can use proxy in order to hold narrower scope from wider scope(e.g prototype from singleton)
All you need is to define the prototype component with the relevant scope and proxyMode
You can read about scoped proxy here.
2) Another thing that I have noticed is that you plan to use multiple autowired annotation.
note that you can use autowire on a list of interface and it will autowire all components that implements this interface as discussed here.
Moreover you can add a platform id to the VehicleBuilder interface and then generate a map in the constructor e.g:
Map<Integer, VehicleBuilder> vehicleBuilders;
#Autowired
public VehicleBuilderFactory(List<VehicleBuilder> vehicleBuilders) {
this.vehicleBuilders = vehicleBuilders.stream()
.collect(Collectors(x -> x.getPlatformId(), x -> x));
}
in that way you can avoid the switch case.

Resources