I am trying to an annotation processor to implicitly make particular method(annotated) arguments final?
My Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MakeFinal_Args {
}
and my annotation processor looks like this:
#SupportedAnnotationTypes("com.walmart.annotations.MakeFinal_Args")
public class Processor extends AbstractProcessor {
private ProcessingEnvironment env;
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Class<?> clazz = Runtime.getRuntime().getClass();
for (Parameter parameter : clazz.getEnclosingMethod().getParameters()) {
parameter.
}
return true;
}
}
Stuck here.Need help with the same. Thanks in advance.
Related
Here is an example
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Annotation {
}
#Configuration
public class Configuration {
#Bean
#Annotation
public Test getTest() {
return new Test();
}
}
public class Test() {
public void test() {
// how can get the annotation `#Annotation` here?
}
}
Here is what I have tried getClass().getAnnotations() but this returns empty array. I can see why since getClass() return Test.class which does not have the annotation. How can I get the method that creates this instance and then get the annotation?
You could, in theory, inspect the current Thread stack to determine the name of your caller, then look up the class definition, locate the method, and read its annotations:
var t = Thread.currentThread().getStackTrace()[2];
var className = t.getClassName();
Class<?> clazz;
try {
clazz = Test.class.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Caller was loaded by a different ClassLoader :-(");
}
for (var method : clazz.getDeclaredMethods()) {
if (method.getName().equals(t.getMethodName())) {
return method.getAnnotation(YourAnnotation.class).value();
}
}
throw new RuntimeException("Method not found - I might have found the wrong class definition");
However:
inspecting the stack is rather slow, in particular if the stack is deep
inspecting the stack is brittle with respect to refactorings (people don't expect that factoring out code into a utility method will change behaviour)
the compiler can not check that the caller provides the required annotation
this only works reliably if all code is loaded by the same ClassLoader
this can not distinguish overloaded methods
This is therefore a rather brittle hack. Are you sure that there is no better option? For instance, requiring the caller to pass the value as a method parameter would have none of these shortcomings ...
You can use ConfigurableListableBeanFactory to get metadata about any Bean by name. Use BeanNameAware interface to retrieve Bean name.
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface CustomAnnotation {
}
#org.springframework.context.annotation.Configuration
public static class ContextConfiguration {
#Bean(name = "TEST")
#CustomAnnotation
public TestObject getTest() {
return new TestObject();
}
}
public class TestObject implements BeanNameAware {
private String beanName;
#Autowired
ConfigurableListableBeanFactory beanFactory;
#Override
public void setBeanName(String name) {
this.beanName = name;
}
public void test() {
CustomAnnotation customAnnotation = (CustomAnnotation) getBeanAnnotation(beanName, CustomAnnotation.class);
}
private Annotation getBeanAnnotation(String beanName, java.lang.Class<? extends Annotation> clazz) {
Annotation annotation = null;
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if( beanDefinition != null && beanDefinition.getSource() instanceof StandardMethodMetadata) {
StandardMethodMetadata metadata = (StandardMethodMetadata) beanDefinition.getSource();
annotation = Arrays.stream(metadata.getIntrospectedMethod().getDeclaredAnnotations()).filter(annot -> annot.annotationType().equals(clazz)).findFirst().orElse(null);
}
return annotation;
}
}
When dealing with CDI in java, I want to inject two instances of two different classes, implementing the same interface.
As I understand, I can inject an instance of a class which does not implement an interface, e.g.:
class MyClass {
// ...
}
class XY {
#Inject MyClass myClass;
}
When my class implements an interface I have to declare the member by the interface name (and specify the concrete implementation):
class MyClass implements MyInterface {
// ...
}
class XY {
#Inject MyInterface myClass;
}
But as soon as I want to inject different implementations, I get the "Api type [...] is not found with the qualifiers" exception:
class MyClassOne implements MyInterface {
// ...
}
class MyClassTwo implements MyInterface {
// ...
}
class XY {
#Inject MyClassOne myClassOne;
#Inject MyClassTwo myClassTwo;
}
I appreciate any ideas what to try or where to continue reading (the obvious keywords for a search on this topic give very unspecific results).
Thanks in advance!
In order to inject different instances, there are different ways to construct and inject beans.
Approach 1:
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, TYPE, METHOD})
public #interface ClassifierOne {
}
#Qualifier
#Retention(RUNTIME)
#Target({FIELD, TYPE, METHOD})
public #interface ClassifierTwo {
}
These qualifiers can be used in your class part of construction parameter injection or setter injection level.
#ClassifierOne
public class MyClassOne implements MyInterface {
// ...
}
#ClassifierTwo
public class MyClassTwo implements MyInterface {
// ...
}
public class XY {
private final MyInterface myClassOne;
private final MyInterface myClassTwo;
#Inject
public XY ( #ClassifierOne MyInterface myClassOne, #ClassifierTwo MyInterface myClassTwo ) {
this.myClassOne = myClassOne;
this.myClassTwo = myClassTwo;
}
}
Approach 2: Use of #Produces
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
public #interface MyClassType {
ClassImplName value();
}
public enum ClassImplName {
CLASS_ONE(MyClassOne.class),
CLASS_TWO(MyClassTwo.class);
private Class<? extends MyInterface> classType;
private ClassImplName(Class<? extends MyInterface> clazz) {
this.classType = clazz;
}
public Class<? extends MyInterface> getClassType(){
return classType;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public #interface ClassType {
ClassImplName value();
}
Above custom qualifiers will allow you to chose type of implementation by removing abibuaty in producer method.
And, you can use below mentioned MyClassFactory to produce interfaces. This mechanism would be efficient as it uses InjectionPoint where the bean is injected.
public class MyInterfaceFactory {
#Produces
#MyClassType
public MyInterface createMyClasses(#Any Instance<MyInterface> instance, InjectionPoint injectionPoint) {
Annotated annotated = injectionPoint.getAnnotated();
ClassType classTypeAnnotation = annotated.getAnnotation(ClassType.class);
Class<? extends MyInterface> classType = classTypeAnnotation.value().getClassType();
return instance.select(classType).get();
}
}
Finally, you can use these generated instances in your class.
public class XY {
#Inject
#ClassType(ClassImplName.CLASS_ONE)
#MyClassType
private MyInterface myClassOne;
#Inject
#ClassType(ClassImplName.CLASS_TWO)
#MyClassType
private MyInterface myClassTwo;
// Other methods using injected beans ...
}
I cannot get bean what I want, when using CDI and Annotation #Qualifier
#Qualifier #interface for Type
#Repeatable(Type.List.class)
#Target({TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Type {
String value();
#Target({TYPE})
#Retention(RetentionPolicy.RUNTIME)
#interface List {
Type[] value();
}
}
and AnnotationLiteral implementation
public class TypeAL extends AnnotationLiteral<Type> implements Type {
private final String type;
public TypeAL(String type) {
this.type = type;
}
#Override
public String value() {
return type;
}
}
#Qualifier #interface for Related
#Target({TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Related {
Class value();
}
and AnnotationLiteral implementation
public class RelatedAL extends AnnotationLiteral<Related> implements Related {
private final Class clazz;
public RelatedAL(Class clazz) {
this.clazz = clazz;
}
#Override
public Class value() {
return clazz;
}
}
When I annotated sth like this:
#Type(TYPE_ONE)
#Type(TYPE_TWO)
#Related(RelatedClassWhichWillDoLogic.class)
public class LogicToRelatedClass implements BaseLogic {}
and when I'd like to get CDI.current().select(BaseLogic.class, new TypeAL(TYPE_ONE), new RelatedAL(RelatedClassWhichWillDoLogic.class)) i go nothing...
Why is that?
what is the version of your CDI? I think repeating qualifiers has been supported from version 2.
https://issues.jboss.org/browse/CDI-471
https://docs.google.com/document/d/1KUaxXIXJ_r-h5UJGIij6I4vrLS7uXkeeeZr2SaRipWQ/edit#
To choose one implementation between different implementations. we could use qualifier members to narrow the list of possible beans. the injection point must be completely matched with the qualifiers on the bean(if you want the exact one). you have two Type annotations on your bean class but use one of them in your CDI.current().select method call.
Instance<BaseLogic> findedBeans = CDI.current().select(BaseLogic.class, new TypeAL("TYPE_ONE"), new TypeAL("TYPE_TWO"), new RelatedAL(RelatedClassWhichWillDoLogic.class));
I tested it In one Weld Java SE program. you can download it from WELD (CDI) + JPA
just in the main method, in App class, add the following line of the code.
UserApplication userApplication = container.instance()
.select(UserApplication.class)
.get();
Instance<BaseLogic> type_one = CDI.current().select(BaseLogic.class, new TypeAL("TYPE_ONE"), new TypeAL("TYPE_TWO"), new RelatedAL(RelatedClassWhichWillDoLogic.class));
I'm using Spring Beans with annotations and I need to choose different implementation at runtime.
#Service
public class MyService {
public void test(){...}
}
For example for windows's platform I need MyServiceWin extending MyService, for linux platform I need MyServiceLnx extending MyService.
For now I know only one horrible solution:
#Service
public class MyService {
private MyService impl;
#PostInit
public void init(){
if(windows) impl=new MyServiceWin();
else impl=new MyServiceLnx();
}
public void test(){
impl.test();
}
}
Please consider that I'm using annotation only and not XML config.
1. Implement a custom Condition
public class LinuxCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux"); }
}
Same for Windows.
2. Use #Conditional in your Configuration class
#Configuration
public class MyConfiguration {
#Bean
#Conditional(LinuxCondition.class)
public MyService getMyLinuxService() {
return new LinuxService();
}
#Bean
#Conditional(WindowsCondition.class)
public MyService getMyWindowsService() {
return new WindowsService();
}
}
3. Use #Autowired as usual
#Service
public class SomeOtherServiceUsingMyService {
#Autowired
private MyService impl;
// ...
}
Let's create beautiful config.
Imagine that we have Animal interface and we have Dog and Cat implementation. We want to write write:
#Autowired
Animal animal;
but which implementation should we return?
So what is solution? There are many ways to solve problem. I will write how to use #Qualifier and Custom Conditions together.
So First off all let's create our custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public #interface AnimalType {
String value() default "";
}
and config:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class AnimalFactoryConfig {
#Bean(name = "AnimalBean")
#AnimalType("Dog")
#Conditional(AnimalCondition.class)
public Animal getDog() {
return new Dog();
}
#Bean(name = "AnimalBean")
#AnimalType("Cat")
#Conditional(AnimalCondition.class)
public Animal getCat() {
return new Cat();
}
}
Note our bean name is AnimalBean. why do we need this bean? because when we inject Animal interface we will write just #Qualifier("AnimalBean")
Also we crated custom annotation to pass the value to our custom Condition.
Now our conditions look like this (imagine that "Dog" name comes from config file or JVM parameter or...)
public class AnimalCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
if (annotatedTypeMetadata.isAnnotated(AnimalType.class.getCanonicalName())){
return annotatedTypeMetadata.getAnnotationAttributes(AnimalType.class.getCanonicalName())
.entrySet().stream().anyMatch(f -> f.getValue().equals("Dog"));
}
return false;
}
}
and finally injection:
#Qualifier("AnimalBean")
#Autowired
Animal animal;
You can move the bean injection into the configuration, as:
#Configuration
public class AppConfig {
#Bean
public MyService getMyService() {
if(windows) return new MyServiceWin();
else return new MyServiceLnx();
}
}
Alternatively, you may use profiles windows and linux, then annotate your service implementations with the #Profile annotation, like #Profile("linux") or #Profile("windows"), and provide one of this profiles for your application.
Autowire all your implementations into a factory with #Qualifier annotations, then return the service class you need from the factory.
public class MyService {
private void doStuff();
}
My Windows Service:
#Service("myWindowsService")
public class MyWindowsService implements MyService {
#Override
private void doStuff() {
//Windows specific stuff happens here.
}
}
My Mac Service:
#Service("myMacService")
public class MyMacService implements MyService {
#Override
private void doStuff() {
//Mac specific stuff happens here
}
}
My factory:
#Component
public class MyFactory {
#Autowired
#Qualifier("myWindowsService")
private MyService windowsService;
#Autowired
#Qualifier("myMacService")
private MyService macService;
public MyService getService(String serviceNeeded){
//This logic is ugly
if(serviceNeeded == "Windows"){
return windowsService;
} else {
return macService;
}
}
}
If you want to get really tricky you can use an enum to store your implementation class types, and then use the enum value to choose which implementation you want to return.
public enum ServiceStore {
MAC("myMacService", MyMacService.class),
WINDOWS("myWindowsService", MyWindowsService.class);
private String serviceName;
private Class<?> clazz;
private static final Map<Class<?>, ServiceStore> mapOfClassTypes = new HashMap<Class<?>, ServiceStore>();
static {
//This little bit of black magic, basically sets up your
//static map and allows you to get an enum value based on a classtype
ServiceStore[] namesArray = ServiceStore.values();
for(ServiceStore name : namesArray){
mapOfClassTypes.put(name.getClassType, name);
}
}
private ServiceStore(String serviceName, Class<?> clazz){
this.serviceName = serviceName;
this.clazz = clazz;
}
public String getServiceBeanName() {
return serviceName;
}
public static <T> ServiceStore getOrdinalFromValue(Class<?> clazz) {
return mapOfClassTypes.get(clazz);
}
}
Then your factory can tap into the Application context and pull instances into it's own map. When you add a new service class, just add another entry to the enum, and that's all you have to do.
public class ServiceFactory implements ApplicationContextAware {
private final Map<String, MyService> myServices = new Hashmap<String, MyService>();
public MyService getInstance(Class<?> clazz) {
return myServices.get(ServiceStore.getOrdinalFromValue(clazz).getServiceName());
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
myServices.putAll(applicationContext.getBeansofType(MyService.class));
}
}
Now you can just pass the class type you want into the factory, and it will provide you back the instance you need. Very helpful especially if you want to the make the services generic.
Simply make the #Service annotated classes conditional:
That's all. No need for other explicit #Bean methods.
public enum Implementation {
FOO, BAR
}
#Configuration
public class FooCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.FOO == implementation;
}
}
#Configuration
public class BarCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.BAR == implementation;
}
}
Here happens the magic.
The condition is right where it belongs: At the implementating classes.
#Conditional(FooCondition.class)
#Service
class MyServiceFooImpl implements MyService {
// ...
}
#Conditional(BarCondition.class)
#Service
class MyServiceBarImpl implements MyService {
// ...
}
You can then use Dependency Injection as usual, e.g. via Lombok's #RequiredArgsConstructor or #Autowired.
#Service
#RequiredArgsConstructor
public class MyApp {
private final MyService myService;
// ...
}
Put this in your application.yml:
implementation: FOO
👍 Only the implementations annotated with the FooCondition will be instantiated. No phantom instantiations. 👍
Just adding my 2 cents to this question. Note that one doesn't have to implement so many java classes as the other answers are showing. One can simply use the #ConditionalOnProperty. Example:
#Service
#ConditionalOnProperty(
value="property.my.service",
havingValue = "foo",
matchIfMissing = true)
class MyServiceFooImpl implements MyService {
// ...
}
#ConditionalOnProperty(
value="property.my.service",
havingValue = "bar")
class MyServiceBarImpl implements MyService {
// ...
}
Put this in your application.yml:
property.my.service: foo
MyService.java:
public interface MyService {
String message();
}
MyServiceConfig.java:
#Configuration
public class MyServiceConfig {
#Value("${service-type}")
MyServiceTypes myServiceType;
#Bean
public MyService getMyService() {
if (myServiceType == MyServiceTypes.One) {
return new MyServiceImp1();
} else {
return new MyServiceImp2();
}
}
}
application.properties:
service-type=one
MyServiceTypes.java
public enum MyServiceTypes {
One,
Two
}
Use in any Bean/Component/Service/etc. like:
#Autowired
MyService myService;
...
String message = myService.message()
After learning about Hibernate Custom Validators, it has given me an interest in one topic, could I possibly create one base annotation wherein I could set which Validator to use?
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = validator().class)
public #interface CustomAnnotation {
public String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends ConstraintValidator<? extends CustomAnnotation, Serializable>> validator();
}
So that I could use #CustomAnnotation in this manner
#CustomAnnotation(validator = CustomConstraintValidator.class, message = "validationMessage")
private Object fieldName;
I would not recommend it but you can do it roughly this way:
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = GenericValidatorBootstrapperValidator.class)
public #interface CustomAnnotation {
public String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends ConstraintValidator<? extends CustomAnnotation, Serializable>> validator();
}
public class GenericValidatorBootstrapperValidator implements ConstraintValidator<CustomAnnotation, Object> {
private final ConstraintValidator validator;
#Override
public void initialize(CustomAnnotation constraintAnnotation) {
Class<? extends ConstraintValidator> validatorClass = constraintAnnotation.validator();
validator = validatorClass.newInstance();
validator.initialize( ... ); //TODO with what?
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return validator.isValid(value, context);
}
}
But again, prefer specific annotations, they are more expressive.
Edit
After your comment, I think what you want is to be able to set different validators based on the return type of the property
#CustomAnnotation
List<String> foo;
#CustomAnnotation
Table bar;
If that's the case, add several validators implementations in the #Constraint annotation.
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {ListValidatorImpl.class, TableValidatorImpl.class, ...})
public #interface CustomAnnotation {
public String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class ListValidatorImpl implements ConstraintValidator<CustomAnnotation, List> {
#Override
public boolean isValid(List value, ConstraintValidatorContext context) {
}
}
public class TableValidatorImpl implements ConstraintValidator<CustomAnnotation, Table> {
#Override
public boolean isValid(Table value, ConstraintValidatorContext context) {
}
}
You can even link a contraint annotation with an implementation via the META/validation.xml file
<constraint-mappings
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.1.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping" version="1.1">
<constraint-definition annotation="org.mycompany.CustomAnnotation">
<validated-by include-existing-validators="true">
<value>org.mycompany.EnumCustomValidatorImpl</value>
</validated-by>
</constraint-definition>
</constraint-mappings>
If you need something more flexible, I think my initial proposal would work. In the GenericValidatorBootstrapperValidator isValid method, you could call the right validator instance based on the object type of the value parameter (via instanceof for example).
Hibernate Validator also offers now a annotation #ScriptAssert which makes the implementation of custom validations easier and helps to avoid plenty lines of code.
Example of use:
#ScriptAssert(lang = "javascript",
script = "_this.capital.equals(_this.capital.toUpperCase)",
message = "capital has not Capital letters")
public class BigLetters {
private String capital;
public String getCapital() {
return capital;
}
public void setCapital(String capital) {
this.capital = capital;
}
}
I don't think you can implement a dynamic validator resolver on top of Hibernate Validator support. It's much better to have a dedicated set of annotation-validator pairs so when you annotate a field with a specific Validation annotation, it's clear what Validator will be used.