Edit:
Perhaps a more concise way to ask this question is: Does Spring provide a way for me to resolve ambiguous candidates at injection time by providing my own listener/factory/decision logic?
In fact, arguably the #Environmental qualifier on the member field below is unnecessary: if an #Inject-ion is ambiguous... let me help? In fact, #ResolveWith(EnvironmentalResolver.class) would be alright too..
When Spring attempts to inject a dependency (using annotations) I understand that I need to #Qualifier an #Inject point if I am to have multiple components that implement that interface.
What I'd like to do is something like this:
class MyFoo implements Foo {
#Inject
#Environmental
private Bar bar;
}
#Environmental(Environment.Production)
class ProductionBar implements Bar {
}
#Environmental({Environment.Dev, Environment.Test})
class DevAndTestBar implements Bar {
}
I would expect that I need to create some kind of ambiguity resolver which would look something (vaguely) like this:
class EnvironmentalBeanAmbiguityResolver {
// set from configuration, read as a system environment variable, etc.
private Environment currentEnvironment;
public boolean canResolve(Object beanDefinition) {
// true if definition has the #Environmental annotation on it
}
public Object resolve(Collection<Object> beans) {
for (Object bean : beans) {
// return bean if bean #Environmental.values[] contains currentEnvironment
}
throw new RuntimeException(...);
}
}
One example of where this would be useful is we have a service that contacts end-users. Right now I just have a hacked together AOP aspect that before the method call to the "MailSender', checks for a "Production" environment flag and if it is not set, it sends the email to us instead of the users email. I'd like to instead of wrapping this in an AOP aspect specific to mail sending, instead be able to differentiate services based on the current environment. Sometime's it is just a matter of "production" or "not production" as I've demonstrated above, but a per-environment definition works too.
I think this can be reused for region too... e.g. #Regional and #Regional(Region.UnitedStates) and so on and so forth.
I'd imagine #Environmental would actually be a #Qualifier that way if you wanted to depend directly on something environmental you could (an #Environmental(Production) bean would likely depend directly on an #Environmental(Production) collaborator - so no ambiguity for lower level items --- same a #Regional(US) item would depend on other #Regional(US) items expiclitly and would bypass my yet-to-be-understood BeanAmbiguityResolver)
Thanks.
I think I solved this!
Consider the following:
public interface Ambiguity {
public boolean isSatisfiedBy(BeanDefinitionHolder holder);
}
#Target({ METHOD, CONSTRUCTOR, FIELD })
#Retention(RUNTIME)
public #interface Ambiguous {
Class<? extends Ambiguity> value();
}
#Target(TYPE)
#Retention(RUNTIME)
public #interface Environmental {
public static enum Environment {
Development, Testing, Production
};
Environment[] value() default {};
}
#Named
public class EnvironmentalAmbiguity implements Ambiguity {
/* This can be set via a property in applicationContext.xml, which Spring
can use place holder, environment variable, etc. */
Environment env = Environment.Development;
#Override
public boolean isSatisfiedBy(BeanDefinitionHolder holder) {
BeanDefinition bd = holder.getBeanDefinition();
RootBeanDefinition rbd = (RootBeanDefinition) bd;
Class<?> bc = rbd.getBeanClass();
Environmental env = bc.getAnnotation(Environmental.class);
return (env == null) ? false : hasCorrectValue(env);
}
private boolean hasCorrectValue(Environmental e) {
for (Environment env : e.value()) {
if (env.equals(this.env)) {
return true;
}
}
return false;
}
}
#Named
public class MySuperDuperBeanFactoryPostProcessor implements
BeanFactoryPostProcessor, AutowireCandidateResolver {
private DefaultListableBeanFactory beanFactory;
private AutowireCandidateResolver defaultResolver;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg)
throws BeansException {
if (arg instanceof DefaultListableBeanFactory) {
beanFactory = (DefaultListableBeanFactory) arg;
defaultResolver = beanFactory.getAutowireCandidateResolver();
beanFactory.setAutowireCandidateResolver(this);
return;
}
throw new FatalBeanException(
"BeanFactory was not a DefaultListableBeanFactory");
}
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return defaultResolver.getSuggestedValue(descriptor);
}
#Override
public boolean isAutowireCandidate(BeanDefinitionHolder holder,
DependencyDescriptor descriptor) {
Ambiguity ambiguity = getAmbiguity(descriptor);
if (ambiguity == null) {
return defaultResolver.isAutowireCandidate(holder, descriptor);
}
return ambiguity.isSatisfiedBy(holder);
}
private Ambiguity getAmbiguity(DependencyDescriptor descriptor) {
Ambiguous ambiguous = getAmbiguousAnnotation(descriptor);
if (ambiguous == null) {
return null;
}
Class<? extends Ambiguity> ambiguityClass = ambiguous.value();
return beanFactory.getBean(ambiguityClass);
}
private Ambiguous getAmbiguousAnnotation(DependencyDescriptor descriptor) {
Field field = descriptor.getField();
if (field == null) {
MethodParameter methodParameter = descriptor.getMethodParameter();
if (methodParameter == null) {
return null;
}
return methodParameter.getParameterAnnotation(Ambiguous.class);
}
return field.getAnnotation(Ambiguous.class);
}
}
Now if I have an interface MyInterface and two classes that implement it MyFooInterface and MyBarInterface like this:
public interface MyInterface {
public String getMessage();
}
#Named
#Environmental({ Environment.Testing, Environment.Production })
public class MyTestProdInterface implements MyInterface {
#Override
public String getMessage() {
return "I don't always test my code, but when I do, I do it in production!";
}
}
#Named
#Environmental(Environment.Development)
public class DevelopmentMyInterface implements MyInterface {
#Override
public String getMessage() {
return "Developers, developers, developers, developers!";
}
}
If I want to #Inject MyInterface I would get the same multiple bean definition error that one would expect. But I can add #Ambiguous(EnvironmentalAmbiguity.class) and then the EnvironmentalAmbiguity will tell which bean definition it is satisfied by.
Another approach would have been to use a List and go through them all seeing if they are satisfied by a given bean definition, this would mean that the dependnecy wouldn't need the #Ambiguous annotation. That might be more "IoC-ish" but I also thought it might perform poorly. I have not tested that.
Related
I have a use case where it would be extraordinarily nice to dynamically instantiate beans (using some kind of factory approach) based on annotation-arguments at the injection point. Specifically, I need to be able to specify a type-argument to the bean-creating factory.
A pretty relevant example would be a JSON deserializer that needs the type which it needs to deserialize to.
I envision either:
#Inject
#DeserializeQualifier(Car.class)
private Deserializer<Car> _carDeserializer;
#Inject
#DeserializeQualifier(Bus.class)
private Deserializer<Bus> _busDeserializer;
.. or simply, if it was possible to sniff the type from the generic type argument:
#Inject
private Deserializer<Car> _carDeserializer;
#Inject
private Deserializer<Bus> _busDeserializer;
The big point here is that I would not know beforehand which types was needed in the project, as this would be a generic tool that many projects would include. So you would annotate your #Configuration class with #EnableDeserializer and could then inject any type deserializer (The factory that makes these deserializers can handle any type, but to be able create one, it would need to know the desired type of the deserialized object - plain generics would not cut it, since Java ain't using reified generics).
So, I'd need to be able to inject into the spring context, or using any other Spring magic tricks, some kind of DeserializerFactory that takes the type argument.
Basically, I need to have Spring invoke the following method based based on either, as in the first example, the qualifier argument (or the entire DeserializeQualifier-instance for that matter), or as in the second example, the generic type argument:
DeserializerFactory {
<T> Deserializer<T> createDeserializer(Class<T> type) { ... }
}
You could create a BeanFactoryPostProcessor to set attributes annotated with a custom annotation. I've set up a small Spring Boot project to play around:
// Custom annotation
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectSomeClassHere {
Class value();
}
// Demo bean
#Component
public class SomeBean {
#InjectSomeClassHere(String.class)
private Class someValue;
public Class getInjectedClass() {
return someValue;
}
}
// The BeanFactoryPostProcessor
#Component
public class SomeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Arrays
.stream(beanFactory.getBeanDefinitionNames())
.filter(beanName -> hasAnnotatedField(beanFactory, beanName))
.forEach(beanName -> {
Object bean = beanFactory.getBean(beanName);
Stream.of(bean.getClass().getDeclaredFields()).forEach(field -> setFieldValue(bean, field));
});
}
private boolean hasAnnotatedField(ConfigurableListableBeanFactory beanFactory, String beanName) {
try {
String className = beanFactory.getBeanDefinition(beanName).getBeanClassName();
if (className == null) {
return false;
}
return Arrays.stream(Class.forName(className).getDeclaredFields())
.anyMatch(field -> field.isAnnotationPresent(InjectSomeClassHere.class));
} catch (ClassNotFoundException e) {
// Error handling here
return false;
}
}
private void setFieldValue(Object filteredBean, Field field) {
try {
// Note: field.isAccessible() is deprecated
if (!field.isAccessible()) {
field.setAccessible(true);
}
// Retrieve the value from the annotation and set the field
// In your case, you could call `createDeserializer(fieldValue);` and set the field using the return value.
// Note that you should change the type of `SomeBean#someValue` accordingly.
Class fieldValue = field.getAnnotation(InjectSomeClassHere.class).value();
field.set(filteredBean, fieldValue);
} catch (IllegalAccessException e) {
// Error handling here
e.printStackTrace();
}
}
}
// A small test to verify the outcome of the BeanFactoryPostProcessor
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeBeanTests {
#Autowired
private SomeBean someBean;
#Test
public void getInjectedClass_shouldHaveStringClassInjected() {
Assert.assertEquals(String.class, someBean.getInjectedClass());
}
}
Please note that this is a very naive implementation and requires further fine tuning. For instance, it scans all attributes in all spring components for the presence of an annotation.
Good luck with your project!
I have a REST service written with Jersey & Spring-Boot. I have written custom validator classes for POST params. I want to unittest the same. I could not figure out how to do it. My Validator looks like below:
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = ValidTaskForCreate.Validator.class)
public #interface ValidTaskForCreate {
String message() default "Invalid Request to create a Task";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
public class Validator implements ConstraintValidator<ValidTaskForCreate, Task> {
#Override
public void initialize(ValidTaskForCreate constraintAnnotation) {
}
#Override
public boolean isValid(final Task task, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
if(task.getName() == null || task.getName().isEmpty()) {
context.buildConstraintViolationWithTemplate("Task name should be specified").addConstraintViolation();
return false;
}
if(task.getTaskType() == null) {
context.buildConstraintViolationWithTemplate("Specify a valid TaskType in the range of [1..3]").addConstraintViolation();
return false;
}
return true;
}
}
}
Now i want to test the isValid() function by passing various Task objects. I am not sure how to call this method now.
I can create instance of Validator class like this,
ValidTaskForCreate.Validator taskValidator = null;
taskValidator = new ValidTaskForCreate.Validator();
To call isValid(), i can use taskValidator.isValid(). But i do not know how to create the ConstraintValidatorContext object to pass as 2nd parameter.
Or is there any way to UnitTest custom validations classes like this?
But i do not know how to create the ConstraintValidatorContext object to pass as 2nd parameter.
Just use Mockito and mock it. Then just verify that the correct methods were called. This is how to test the behavior of the unit when dependencies are involved.
private ConstraintValidatorContext context;
private ConstraintValidatorContext.ConstraintViolationBuilder builder;
#Before
public void setup() {
// mock the context
context = Mockito.mock(ConstraintValidatorContext.class);
// context.buildConstraintViolationWithTemplate returns
// ConstraintValidatorContext.ConstraintViolationBuilder
// so we mock that too as you will be calling one of it's methods
builder = Mockito.mock(ConstraintValidatorContext.ConstraintViolationBuilder.class);
// when the context.buildConstraintViolationWithTemplate is called,
// the mock should return the builder.
Mockito.when(context.buildConstraintViolationWithTemplate(Mockito.anyString()))
.thenReturn(builder);
}
#Test
public void test() {
// call the unit to be tested
boolean result = ..isValid(badTask, context);
// assert the result
assertThat(result).isFalse();
// verify that the context is called with the correct argument
Mockito.verify(context)
.buildConstraintViolationWithTemplate("Task name should be specified");
}
Note the use of Mockito directly. In most cases you will probably just use static imports to make it less verbose. I just wanted to make it more readable
This is the best way I found using standard Spring unit testing with no need to mock anything.
#RunWith(SpringRunner.class)
#SpringBootTest(classes= {ValidationAutoConfiguration.class})
public class AllowedValuesValidatorTest {
#Autowired
private Validator validator;
#Test
public void testIsValid() {
ObjectToBeValidated obj = // create object
Set<ConstraintViolation<ObjectToBeValidated>> violations = validator.validate(obj);
boolean violationsFound =
violations.stream().anyMatch(v -> v.getConstraintDescriptor().getAnnotation().annotationType().equals(
NonNullLowercaseLettersOrNumbersOnly.class));
assertThat(externalIdViolationFound).isTrue();
}
}
It's the ValidationAutoConfiguration.class as the configuration of the test that does the heavy lifting. This will exercise all validation on the ObjectToBeValidated and you can search the violations for just that one you're testing for.
I want to implement an annotation which registers classes (not instances of classes) with a factory as soon as the application is started. I am using Spring Framework 4.2.7.
Consider a system with a dashboard and multiple widgets. The dashboard has a configuration file which contains a list of widgets to display for the current user. When displayed it reads the configuration and creates the widgets. The widgets will receive additional parameters from the configuration.
Here is a bit of code illustrating this:
public class TestDashboard implements Dashboard {
public void dashboardPreDisplay() {
List<String> widgets = getWidgetList(/* current user in session */);
for (String widgetId : widgets) {
// create instance of DashboardWidget with given ID
DashboardWidget x = widgetFactory.createWidget(widgetId);
}
}
public List<String> getWidgetList(String user) {
// load list of IDs of DashboardWidgets to be displayed for the user
}
#Autowired
private WidgetFactory widgetFactory;
}
#Service
public class WidgetFactory {
public DashboardWidget createWidget(String widgetId) {
// look up Class<> of DashboardWidget with given id in widgetClasses
// construct and initialize DashboardWidget
}
private HashMap<String, Class<?>> widgetClasses;
}
When implementing my widgets I don't want to deal with registering the widget with the factory class. Ideally I would just annotate the widget like that:
#DashboardWidget(id = "uniqueId")
public class DashboardWidgetA implements DashboardWidget {
// ...
}
When the application starts it should scan the classpath for #DashboardWidget annotations and register the classes with the factory, so that the widgets can be constructed by giving the createWidget-method the id of the widget.
At the moment I am a little bit confused. I think Spring has every tool on board to achieve this behavior. But I cannot think of a way how to do it.
Do you have some advice for me?
Nothing prevents you to create your custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface DashboardWidget {}
Then you can annotate your Widget's classes and make them spring beans. You have to keep in mind if you want to have them as singletons (scope=singleton) , or separate instances per user (scope=prototype).
You have to implement:
public class WidgetInitializationListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
String originalClassName = getOriginalClassName(beanDefinitionName, event);
if (originalClassName != null) {
Class<?> clazz = Class.forName(originalClassName);
if (hasWidgetAnnotation(clazz)) {
registerSomewhereYourWidget(context, beanDefinitionName, originalClassName);
}
}
}
}
private String getOriginalClassName(String name, ContextRefreshedEvent event) {
try {
ConfigurableListableBeanFactory factory =
(ConfigurableListableBeanFactory)event.getApplicationContext().getAutowireCapableBeanFactory();
BeanDefinition beanDefinition = factory.getBeanDefinition(name);
return beanDefinition.getBeanClassName();
} catch (NoSuchBeanDefinitionException e) {
LOG.debug("Can't get bean definition for : " + name);
return null;
}
}
So mostly here is nothing to do with spring except you just run through your beans to find annotated ones.
I have an idea for a specific event handling based on generics, but seems like Weld can't handle them. I asked google but couldn't find an alternative CDI extension for this.
Question: is there a CDI extension, that can handle event propagation of generic-typed events?
In the following the explicit problem I have.
I have three general events, EntityCreated, EntityChanged and EntityDeleted. The base class for them is defined like this:
public abstract class DatabaseEvent<TargetType> {
public TargetType target;
public DatabaseEvent(TargetType target) {
this.target = target;
}
}
The events then are simple inherited classes:
public class EntityCreatedEvent<TargetType> extends DatabaseEvent<TargetType> {
public EntityCreatedEvent(TargetType target) {
super(target);
}
}
I fire them like this:
public abstract class MyHome<EntityType> {
private EntityType entity;
#Inject
Event<EntityCreatedEvent<EntityType>> entityCreatedEvent;
public void fireCreatedEvent() {
EntityCreatedEvent<EntityType> payload = new EntityCreatedEvent<EntityType>(entity);
entityCreatedEvent.fire(payload);
}
}
I want to observe them like this:
public void handleProjectCreated(#Observes EntityCreatedEvent<Project> event) { ... }
When launching the server Weld tells me it can't handle generic-typed events. The CDI-way of doing things would be to use additional qualifiers instead of the generics to distiguish them, e.g.:
public void handleProjectCreated(#Observes #ProjectEvent EntityCreatedEvent event) { ... }
However, I fire the events from that MyHome base class, where I can't just fire with the #ProjectEvent: it might not be a project but another type.
My solution up to now is to skip that typing altogether and handle them like this:
public void handleProjectCreated(#Observes EntityCreatedEvent event) {
if(event.target instanceof Project) { ... }
}
This solution is okay, but not perfect.
I guess you can do this with dinamically binding qualifier members. This is what your code would look like:
public abstract class MyHome {
private EntityType entity;
#Inject
Event<EntityCreatedEvent> entityCreatedEvent;
public void fireCreatedEvent() {
entityCreatedEvent.select(getTypeBinding()).fire(new EntityCreatedEvent(entity));
}
private TypeBinding getTypeBinding() {
return new TypeBinding() {
public Class<? extends EntityType> value() {return entity.getClass();}
};
}
#Qualifier
#Target({ PARAMETER, FIELD })
#Retention(RUNTIME)
public #interface EntityTypeQualifier {
Class<? extends EntityType> value();
}
public abstract class TypeBinding extends AnnotationLiteral<EntityTypeQualifier> implements EntityTypeQualifier {}
//Observers
public void handleEntityType1Created(#Observes #EntityTypeQualifier(EntityType1.class) EntityCreatedEvent event) {}
public void handleEntityType2Created(#Observes #EntityTypeQualifier(EntityType2.class) EntityCreatedEvent event) {}
As this CDI issue points it is not possible to fire an without having the type of T at runtime.
But, if you have the type of T (i.e. you have an instance) you can use the Event as an Instance, and select the event to be fired using a dynamic type literal.
I'm trying to use CDI events in my backend services, on JBoss AS6 - ideally with maximum code reuse.
I can see from the docs I can cut down on the qualifier annotation classes I have to create by using a qualifier with members e.g.
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface Type {
TypeEnum value();
}
I can observe this with
public void onTypeAEvent(#Observes #Type(TypeEnum.TYPEA) String eventMsg) {...}
So far, so good. However, to further cut down on the number of classes needed, I want to have one EventFirer class, where the qualifier of the event thrown is dynamic. Not a problem with qualifiers without members:
public class DynamicEventFirer {
#Inject #Any private Event<String> event;
public void fireEvent(AnnotationLiteral<?> eventQualifier){
event.select(eventQualifier).fire("FIRED");
}
}
then called like
dynamicEventFirer.fireEvent(new AnnotationLiteral<Type>() {});
But what about when the qualifier should have members? Looking at the code for AnnotationLiteral, it's certainly setup for members, and the class element comment has the example:
new PayByQualifier() { public PaymentMethod value() { return CHEQUE; } }
This makes sense to me - you're overriding the value() method of the annotation interface. However, when I tried this myself:
dynamicEventFirer.fireEvent(new AnnotationLiteral<Type>() {
public TypeEnum value() {
return TypeEnum.TYPEA;
}
});
I receive the exception
java.lang.RuntimeException: class uk.co.jam.concept.events.MemberQualifierEventManager$1 does not implement the annotation type with members uk.co.jam.concept.events.Type
at javax.enterprise.util.AnnotationLiteral.getMembers(AnnotationLiteral.java:69)
at javax.enterprise.util.AnnotationLiteral.hashCode(AnnotationLiteral.java:281)
at java.util.HashMap.getEntry(HashMap.java:344)
at java.util.HashMap.containsKey(HashMap.java:335)
at java.util.HashSet.contains(HashSet.java:184)
at org.jboss.weld.util.Beans.mergeInQualifiers(Beans.java:939)
at org.jboss.weld.bean.builtin.FacadeInjectionPoint.<init>(FacadeInjectionPoint.java:29)
at org.jboss.weld.event.EventImpl.selectEvent(EventImpl.java:96)
at org.jboss.weld.event.EventImpl.select(EventImpl.java:80)
at uk.co.jam.concept.events.DynamicEventFirer.fireEvent(DynamicEventFirer.java:20)
Can anyone see what I'm doing wrong? MemberQualifierEventManager is an ApplicationScoped bean which calls on DynamicEventFirer to fire the event.
Thanks,
Ben
There's a slightly cleaner way to do it based on your post:
public class TypeQualifier extends AnnotationLiteral<Type> implements Type{
private TypeEnum type;
public TypeQualifier(TypeEnum t) {
this.type = t;
}
public TypeEnum value() {
return type;
}
}
then just fire like this:
dynamicEventFirer.fireEvent(new TypeQualifier(TypeEnum.TYPEA));
You need to declare an abstract TypeQualifier that extends AnnotationLiteral and implements Type
abstract class TypeQualifier extends AnnotationLiteral<Type> implements Type{}
and use it like this
dynamicEventFirer.fireEvent(new TypeQualifier() {
public TypeEnum value() {
return TypeEnum.TYPEA;
}
});
and later if you want to fire an event with TypeEnum.TYPEB
dynamicEventFirer.fireEvent(new TypeQualifier() {
public TypeEnum value() {
return TypeEnum.TYPEB;
}
});