Spring creating beans of same interface - java

Consider a package which many classes all implement an interface Policy. The Policy has one method canAccess. There are 100+ policies like MenNotAllowedPolicy , ChiledNotAllowedPolicy ,... which all implement Policy
A property file describe which policies are applied to which service, for example:
BarrowBook=MenNotAllowedPolicy
BarrowMovie=MenNotAllowedPolicy,ChiledNotAllowedPolicy
To uses these polices there is a simple loop, which gets a service name an person, loop the property file and run the polices for persons. The main part of this code is:
public canPersonAccessService(aPerson , aService){
//the listPolicy will be read from property file
for(String policyClassName: listPolicy){
Class<?> clazz = Class.forName("foo.bar.Policies"+ policyClassName);
Policy policy = (policy) clazz.newInstance();
policy.canAccess(aPerson);
}
}
Although Ii can make better by catching the Policy classes but I wonder if it is possible to do it easier with Spring ?! I decided a HashMap with ClassName as a key and the class instance as value, but how can I create it ?!
This a mimic of my problem :)

Define an interface called Policy as base interface for all policy implementations
interface Policy {
boolean canAccess(User u);
}
Have one Spring Bean for each of the policy implementations - make sure you name the bean in #Component and ensure that it matches the name used in your properties file
#Component("MenNotAllowedPolicy")
public static class MenNotAllowedPolicy implements Policy {
public boolean canAcces(User u) {
...
}
}
Make the class that checks the policies also a Spring Bean, and have Spring ApplicationContext autowired in it
#Component
public static class PolicyChecker {
...
#Autowired
private ApplicationContext appContext;
...
public boolean canPersonAccessService(User person, ....) {
for(String policyName: listPolicy) {
Policy policy = appContext.getBean(policyName, Policy.class);
....
policy.canAccess(person);
....
}
}
}
We look up policy by the its bean name, while also ensuring that bean implements Policy interface as indicated by second parameter of getBean method.
Hope this helps!

Related

How to retrieve custom annotation fields?

I would like to implement a custom annotation that could be applied to a class (once inside an app), to enable a feature (Access to remote resources). If this annotation is placed on any config class, it will set the access for the whole app. So far it isn't that hard (see example below), but I want to include some definition fields in the #interface that will be used in the access establishing process.
As an example, Spring has something very similar: #EnableJpaRepositories. Access is enabled to the DB, with parameters in the annotation containing definitions. For example: #EnableJpaRepositories(bootstrapMode = BootstrapMode.DEFERRED)
So far, I have:
To create only the access I'm using something like that:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Import(AccessHandlerConfiguration.class)
public #interface EnableAccessHandlerAutoconfigure {
String name() default "";
}
Using it:
#EnableAccessHandlerAutoconfigure{name="yoni"}
#Configuration
public class config {}
AccessHandlerConfiguration is a configuration class that contains beans that establish the connection.
The problem I'm having is that I don't know how to retrieve the field name's value. What should I do?
Retrieving the value may be accomplished as follows:
this.getClass().getAnnotation(EnableAccessHandlerAutoconfigure.class).name()
To expand on my comment with an actual example configuration class that uses this:
#EnableAccessHandlerAutoconfigure(name="yoni")
#Configuration
public class SomeConfiguration {
#Bean
SomeBean makeSomeBean() {
return new SomeBean(this.getClass().getAnnotation(EnableAccessHandlerAutoconfigure.class).name());
}
}
This is how you get the value of name, as to what you are going to do next, that depends on you.
After a long research, I found a way: There is a method in Spring's ApplicationContext that retrieves bean names according to their annotations getBeanNamesForAnnotation, then get the annotation itself findAnnotationOnBean, and then simply use the field getter.
#Configuration
public class AccessHandlerConfiguration {
private final ApplicationContext applicationContext;
public AccessHandlerConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
String[] beansWithTheAnnotation = applicationContext.getBeanNamesForAnnotation(EnableRabbitAutoconfigure.class);
for (String beanName : beansWithTheAnnotation) {
EnableRabbitAutoconfigure annotationOnBean = applicationContext.findAnnotationOnBean(beanName, EnableRabbitAutoconfigure.class);
System.out.println("**********" + beanName + "*********************" + annotationOnBean.name() + "*******************");
}
}
}
Results:
**********config*********************yoni*******************

How to register POJO factory *methods* with a Spring component?

I have a Spring design problem that I've not come across before:
I have an application that manages a bunch of POJOs, I would like to register a factory for each of these POJO classes so a centralised #Component can do the management. Something like this:
class RouteLink implements Link {
}
class HiddenLink implements Link {
}
... lots of others
interface Loader {
Link load(Element xml);
}
#Component
class Manager {
private final Map<String, Loader> loaders = ...
public void create(Element xml) {
// Create link
final String type = ... // from XML
final Loader loader = loaders.get(type);
final Link link = loader.load(xml);
// Do something with the link we created
...
}
}
(The links are created from an XML element, but that's not important to the question - it could be from text, or other POJOs, etc).
Now I would like to co-locate the POJO and it's associated Loader. I could create a #Configuration class that creates a #Bean for each type but that violates the co-location and requires the developer (me!) to continually switch between source files.
Ideally I would like to do something like the following:
class RouteLink implements Link, Loader {
...
// <--- some Spring magic here to register this as a factory
public Link load(Element xml) {
...
}
}
The load() method cannot be a #Bean or a #Component because we are dealing with POJOs. So I'm forced to create a new class just to call a single method:
class RouteLink ... {
...
#Component
public class RouteLinkLoader implements Loader {
public Link load(Element xml) { .... }
}
}
and the loaders are registered with the manager like this:
public Manager(ApplicationContext ctx) {
loaders = ctx.getBeansOfType(Loader.class);
}
It works, sort of, but I can't help feeling I'm missing something. Is there any way of registering the load methods as components?
Notes:
I've tried denoting the POJO classes with #Configuration but that
then treats the class as a bean. But without that or another
stereotype annotation any #Bean in a POJO is not scanned.
I could just programatically register each POJO loader with the
manager but that seems a bit daft when using a DI framework.
The manager looks up the loader by name which is why we look them
up from the context rather than simply auto-wiring.
I've been developing with Spring for years but seem to have a blind-spot with this design (or lack of it!) Is there a better approach?
I'm not sure if I understand your problem correctly, what you want is to create objects that will be managed by Spring? Have you looked at Spring BeanFactory?
So it looks like there is no magic solution to this if we want to co-locate the factory and POJO code in the same source file, basically one has to create a separate #Component to register the POJO factories.
This means we have to wrap every factory method in a class such that it can be component scanned, rather than (for example) just having a static class constant to do it, something like this:
class PojoClass {
#MagicAnnotationHere
public static final Loader LOADER = xml -> new PojoClass(...);
}
Can't be done, so the factory method becomes a first-class component class which means a lot more code:
class PojoClass {
...
#Component
class PojoLoader implements Loader {
#Override
public void load(Element xml) { ... }
}
}
On the positive side the factories can be #AutoWired into the manager component as follows:
#Component
class Manager {
#Autowired
private final Map<String, Loader> loaders = new HashMap<>();
Spring auto-magically populates the map indexed by bean name, which is precisely what I needed to lookup the factory by name. I'll call that a score-draw!

How can I achieve annotation-based collection merging in Spring?

I am trying to initialize a Spring component with a set of all beans of a certain type (well really, anything I can iterate).
The Spring core documentation talks about collection merging, but only in the context of annotation-based configuration.
Suppose I have the following configuration
#Configuration
public class MyConfig {
#Bean
public SomeInterface single() {
return new SomeInterface() {};
}
#Bean
public Set<SomeInterface> multi() {
return Collections.singleton(
new SomeInterface() {}
);
}
}
Where the interface is defined as
public interface SomeInterface {}
I would like this component to get an aggregate of both beans - some collection containing both anonymous classes.
#Component
public class MyComponent {
public MyComponent(Set<SomeInterface> allInterfaces) {
System.out.println(allInterfaces.size()); // expecting 2, prints 1
}
}
I see why Spring has come to the result it has; it sees this method is expecting a Set<SomeInterface> and MyConfig::multi is a bean of type Set<SomeInterface>, so it autowires with that.
If I change the signature to Collection<SomeInterface>, it autowires with MyConfig::single. Again, I see why: there's nothing matching exactly, but there's beans of type SomeInterface (in this case, just one) so it constructs a temporary collection of them and autowires with that. Fine, but not what I'm after.
I would like the solution to be extensible so that if another bean is added, the dependent component does not need to change. I've tried using two parameters, each with a #Qualifier, and that works but is not extensible.
How can I get this to work?
As you already mentioned, MyConfig::multi is a bean of type Set<SomeInterface>, so autowiring Collection<Set<SomeInterface>> would give you all of those sets. The following should work
public MyComponent(Collection<SomeInterface> beans,
Collection<Set<SomeInterface>> beanSets) {
// merge both params here
}
If you need all implementations in multiple places it might make sense to define another bean containing the merged collection and autowire that bean:
static class SomeInterfaceCollection {
final Set<SomeInterface> implementations;
SomeInterfaceCollection(Set<SomeInterface> implementations) {
this.implementations = implementations;
}
}
#Bean
public SomeInterfaceCollection collect(Collection<SomeInterface> beans,
Collection<Collection<SomeInterface>> beanCollections) {
final HashSet<SomeInterface> merged = ...
return new SomeInterfaceCollection(merged);
}

How to access configuration in a Lagom service at startup?

I am migrating my current app in Spring/J2EE to Lagom. I am working in Java. I need to read variables from the configuration (application.conf in resources folder). In the implementation module, I try to inject configuration as a class variable like this
#Inject
private Configuration config
but when I access this config object in the constructor, it gives null pointer exception.
The whole code is like this
import play.Configuration;
public class SomeServiceImpl implements SomeService {
#Inject
private Configuration config;
public SomeServiceImpl() {
//getting configuration from application.conf
// gives exception as config is null.
String key = config.getString(“key”);
}
#Override
public ServiceCall<Request, Response> send() {
//works here, does not give exception
String key = config.getString(“key”);
}
}
Sorry, I should have been clear from the beginning. I have edited the original question. I get null pointer exception when I try to read from configuration object in constructor but I am able to use it in service call implementation. I want some way in which I can access the configuration in application.conf at startup and possibly store in some config class which can be accessed anywhere later.
In Java, when an object is instantiated, the first thing that happens (before anything else can possibly happen) is the constructor is invoked. After that, frameworks like Guice (which Lagom uses) are free to inject things, but they can't do it until the constructor has been invoked. So, all your #Inject annotated fields will be null when the constructor is invoked, there is nothing you can do to work around that.
So, don't use field injection, use constructor injection, eg:
import play.Configuration;
public class SomeServiceImpl implements SomeService {
private final Configuration config;
#Inject
public SomeServiceImpl(Configuration config) {
this.config = config;
String key = config.getString("key");
}
#Override
public ServiceCall<Request, Response> send() {
String key = config.getString("key");
}
}
Constructor injection is not just recommended for this use case, you should be using it everywhere, it avoids all these potential issues.

Configure Implementation Class of Abstract Factory with Spring

For my application, I have a Scale interface and multiple classes implementing this interface, for example NormalizedScale, LogScale, etc. In one of my Services, I need to create many Scales, and I want to use Spring to define which implementation of the Scale it should create. How would I implement something like this?
--
I was thinking to create a factory ScaleFactory, like in the Abstract Factory Pattern, which I could call ScaleFactory.getScale() to get a Scale of whichever implementation I configured in the Spring XML:
class ScaleFactory {
Class<? extends Scale> scaleImplClass;
public static Scale getScale() {
return scaleImplClass.newInstance();
}
}
Scale myScale = ScaleFactory.getScale();
But with that approach, how could I configure which implementation the ScaleFactory should use from Spring XML?
--
An alternative would be to make the ScaleFactory a #Service, and then autowire the ScaleFactory into my service:
#Autowired
ScaleFactory scaleFactory;
...
Scale myScale = scaleFactory.getScale();
Then I can use an autowired property in the ScaleFactory to define the scaleImplClass. But that seems weird because my Factory is also a Service and I have an instance of that factory.
--
Another approach would be to have the Class scaleImplementationClass property in my service instead of the ScaleFacotry and use the ScaleFactory like so:
#Value("${scaleImplementationClass}")
Class scaleImplementationClass
...
Scale myScale = ScaleFactory.getScale(scaleImplementationClass);
But then the factory is quite pointless because I could also just as well run scaleImplementationClass.newInstance().
There are a couple of different Spring-like ways you can handle this. The approach I have personally gone for looks a bit like this:
public interface ScaleFactory {
public Scale newInstance();
public String type();
}
public class FirstScaleFactory implements ScaleFactory {
public Scale newInstance() {
return new FirstScale();
}
public String type() {
return "first";
}
}
public class SecondScaleFactory implements ScaleFactory {
public Scale newInstance() {
return new SecondScale();
}
public String type() {
return "second";
}
}
public class ScaleManager {
private final Map<String, ScaleFactory> factories;
#Autowired
public ScaleManager(List<ScaleFactory> factories) {
this.factories = factories.stream()
.collect(Collectors.toMap(f -> f.type(), Function::identity));
}
public Scale newInstance(String type) {
return Optional.ofNullable(factories.get(type))
.map(factory -> factory.newInstance())
.orElseThrow(IllegalArgumentException::new);
}
}
With this approach, your ScaleManager is a standard Spring bean that can be wired into any class that needs a scale instance. At initialization time, it gets all ScaleFactories that are defined in the Spring context, and autowires them in as a List<ScaleFactory>, which is then converted to a Map (where the ScaleFactory type is the key). This avoids you needing to worry about class names of Scale, and gives your the ability to change them later (as long as you keep the type key consistent)`
Your ScaleFactory implementations can then do whatever they need to do. For example, if you have one type of Scale that you know is immutable, you can have the factory return the same instance every time. Alternatively you can have every invocation return a separate instance - the instantiation of the Scale is up to the implementation-dependent factory.
You can simply use "Qualifiers" which is basically going to point to a specific "named" bean. By default the bean names are the name of your classes, with the first letter in lower case (MyClass -> myClass). If you want to define your own names you can do as follow :
#Service("customizedBeanName")
You would end up doing something like this :
#Autowired
#Qualifier("logScale")
private Scale logScale;
#Autowired
#Qualifier("anotherScale")
private Scale anotherScale;
As for spring 5.x there's a simpler and cleaner way of doing this. I have decided to use #ConditionalOnProperty annotation but you may choose any #Conditional* of your preference.
Here's the thing, I've have simplified to extreme:
public interface MyService {}
#Service
#ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Some")
public class SomeService implements MyService {}
#Service
#ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Foo")
public class FooService implements MyService {}
#Service
public class SimpleService {
#Autowired
SimpleService(MyService service) {
// service instance will depend on configuration
}
}
I'm using springboot so I've decided to use application.properties in order to set values via environment variables like this:
myService.Impl=${MY_SERVICE_IMPL}
Then, I have a fully dynamic injection based on environment variables that may be passed to a docker container for instance.

Categories