javaslang List.of() on cdi Instance - java

I have multiple class with a Qualifier that I created:
#ServiceComponent(restPath = "/trucks")
public class TruckService {
}
#ServiceComponent(restPath = "/cars")
public class CarService {
}
here is the Qualifier (not important for the question)
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({TYPE, FIELD})
public #interface ServiceComponent {
public boolean exposeAsRest() default true;
#Nonbinding public String restPath() default "";
#Nonbinding public String restGetPrefix() default "get,find,all";
#Nonbinding public String restPostPrefix() default "create,new,post";
}
in another class, I inject those instance using javax.enterprise.inject.Instance<>
class SomeConfigurationClasss {
#Inject
#ServiceComponent()
Instance<Object> _restComponents;
#Override
public void iterate() throws Exception {
//iterate
for(Object obj : _restComponents){
somefuncion(obj);
}
//List.of(_restComponents)
//.flatMap(obj -> somefuncion(obj));
}
}
if I execute the "normal" iteration (for...) I get the Object (TruckService or CarService) given as parameter to the somefunction().
but if I use javaslang's List.of(...) I get the Instance itself. Which I think it's the expected behavior
Is there a possibility to use List.of on a Instance that can contain one or multiple bean (depending on the injection binding). (I already try to call iterator(), select() on the Instance)

Instance<T> extends Iterable<T> so you should use List#ofAll(Iterable)

Related

Access annotation attributes from custom oval annotation

Is it possible, when using custom oval annotation and custom class for check, to access the annotation and retrieve the used annotation attributes ?
Reference for oval: https://sebthom.github.io/oval/USERGUIDE.html#custom-constraint-annotations
Minimal example
Lets assume we have class Foo.
It has two annotated fields.
Each time, the annotation has a different myValue – a and b.
class Foo {
#CustomAnnotation(myValue = "a")
public String first;
#CustomAnnotation(myValue = "b")
public String second;
}
This is the annotation.
It is noted that a check should be performed using MyCheck.class, also setting some default value for myValue.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Constraint(checkWith = MyCheck.class)
public #interface CustomAnnotation {
String myValue() default "";
}
Now we want to use oval to validate this field.
Most importantly, we want to extract the value a or b from the annotation's myValue and use it inside our validation logic.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
// how to get the value of `myValue`, which is `a` or `b` or empty string as default
}
}
What I have tried and failed:
validatedObject is Foo.class. You can easily get its fields and annotations. However, there is no way to differentiate between the two annotations.
valueToValidate is in this case String value – what first or second holds.
context not useful, you can get compile time type from it, which is String
validator not useful ?
After some digging in the superclass I have found that you can override method
configure
This method gets as the only parameter the annotation that is currently being checked at the field.
You can then read the myValue.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
private String myValue;
#Override
public void configure(CustomAnnotation customAnnotation) {
super.configure(customAnnotation);
this.myValue = customAnnotation.myValue();
}
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
if (myValue.equals("a")) {}
else if (myValue.equals("b")){}
else {}
}

Can spring annotation access method parameters?

Consider a UrlValidator method annotation that tests if a given url is valid before calling a method.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface UrlValdator{
String value();
}
This is working fine when routes are static and known ahead of time. For example:
#UrlValidator("http://some.known.url")
public void doSomething();
But this is not very flexible. For example, what if the route was implicit in the doSomething() method signature? Could I somehow access it form the Spring Expression Language, or some other means? For example, this doesn't work but is what I'm shooting for
#UrlValidator("#p1")
public void doSomething(String url)
or
#UrlValidator("#p1.url")
public void doSomething(Request request)
Is it possible to make annotations dynamic this way?
Related
This is the closest I've found, but the thread is old and the accepted answer is quire cumbersome/hard to follow. Is there a minimal working example/updated way to do this?
I'm not entirely sure if that's what you had in mind, but i can suggest using Spring AOP as it can give you a lot of flexibility.
Since you've mentioned in one of the comments that you're already using Spring AOP, I'm going to assume that you've added spring-boot-starter-aop as a dependency and that you've enabled support for handling components marked with #Aspect by annotating one of your config classes with #EnableAspectJAutoProxy
For example, having defined annotations as such:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface EnsureUrlValid {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface UrlToVerify {
}
I can use them in a sample spring component as follows:
#Component
public class SampleComponent {
private static final Logger logger = LogManager.getLogger(SampleComponent.class);
#EnsureUrlValid
public void fetchData(String url) {
logger.info("Fetching data from " + url);
}
#EnsureUrlValid
public long fetchData(Long id, #UrlToVerify String url) {
logger.info("Fetching data for user#" + id + " from " + url);
// just to show that a method annotated like this can return values too
return 10L;
}
#EnsureUrlValid
public void fetchDataFailedAttempt() {
logger.info("This should not be logged");
}
}
And here's a sample "processor" of the EnsureUrlValid annotation. It looks for the annotated methods, tries to extract the passed-in url and depending on whether the url is valid or not, it proceeds with invoking the method or throws an exception. It's simple but it shows that you have complete control over the methods that you've annotated.
#Aspect
#Component
public class UrlValidator {
#Around(value = "#annotation(EnsureUrlValid)")
public Object checkUrl(ProceedingJoinPoint joinPoint) throws Throwable {
final Optional<String> urlOpt = extractUrl(joinPoint);
if (urlOpt.isPresent()) {
final String url = urlOpt.get();
if (isUrlValid(url)) {
return joinPoint.proceed();
}
}
throw new RuntimeException("The passed-in url either could not be resolved or is not valid");
}
private Optional<String> extractUrl(JoinPoint joinPoint) {
Object[] methodArgs = joinPoint.getArgs();
Object rawUrl = null;
if (methodArgs.length == 1) {
rawUrl = methodArgs[0];
}
else if (methodArgs.length > 1) {
// check which parameter has been marked for validation
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Parameter[] parameters = method.getParameters();
boolean foundMarked = false;
int i = 0;
while (i < parameters.length && !foundMarked) {
final Parameter param = parameters[i];
if (param.getAnnotation(UrlToVerify.class) != null) {
rawUrl = methodArgs[i];
foundMarked = true;
}
i++;
}
}
if (rawUrl instanceof String) { // if rawUrl is null, instanceof returns false
return Optional.of((String) rawUrl);
}
// there could be some kind of logic for handling other types
return Optional.empty();
}
private boolean isUrlValid(String url) {
// the actual validation logic
return true;
}
}
I hope it's somewhat helpful.
Short answer: Yes.
Long answer:
ElementType specifies the target of the annotation, which can be the following: ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE, and TYPE_PARAMETER. Were are interested in PARAMETER here. Since we want from the compiler the run our code, RetentionPolicy.RUNTIME is fine for the retention type.
Next we have to add #Constraint annotation, which according to the documentation:
Marks an annotation as being a Bean Validation constraint.
This means, Spring will pick up your parameter and validate it in runtime. The last thing we have to do is to implement the validation itself which implies creating a class which implements ConstraintValidator interface.
Putting it all together:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = UrlValidatorImplementation.class)
public #interface UrlValidator{
String message() default "Invalid url";
}
Implementation of the UrlValidatorImplementation class:
public class UrlValidatorImplementation implements ConstraintValidator<UrlValidator, String> {
#Override
public void initialize(UrlValidator annotation) {
// initialization, probably not needed
}
#Override
public boolean isValid(String url, ConstraintValidatorContext context) {
// implementation of the url validation
}
}
Usage of the annotation:
public void doSomething(#UrlValidator url) { ... }

Receive annotation value from annotated field

Is it possible to receive annotation value inside a field, that was annotated?
Imagine that I have this interface:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String value();
}
And I have such implementation:
class SomeClass {
#MyAnnotation("Annotation")
private MyClass myClass;
}
What I want to understand is: is it possible to receive value of MyAnnotation inside MyClass? I want to implement a method inside class MyClass, which will return a value of assigned annotation. So, that myClass.getAssignedAnnotationValue() will return "Annotation".
If it is not possible, please inform me.
is it possible to know annotation value inside annotated field
It's not possible.
You may have 2 different classes
class SomeClass {
#MyAnnotation("Annotation")
private MyClass myClass;
public SomeClass(MyClass myClass) {
this.myClass=myClass;
}
}
and
class SomeClassNo Annotation {
private MyClass myClass;
public SomeClassNo(MyClass myClass) {
this.myClass=myClass;
}
}
Then you create an instance of MyClass
MyClass instance = new MyClass();
then 2 classes instances
new SomeClass(instance) and new SomeClassNo(instance) both have reference to the same instance. So the instance does not know whether the reference field annotated or not.
The only case when it is possible is to pass somehow the container reference to MyClass.
There is no straight forward way of implementing what you are asking.
WorkAround:
Limitations:
This workaround doesn't enforce any kind of compile time check and it is completely your responsibility to handle it.
This only works if MyClass is going to be a spring bean.
class MyClass {
public String annotatedValue;
}
You can write a Spring BeanPostProcessor the following way.
public class SampleBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field instanceof MyClass && field.isAnnotationPresent(MyAnnotation.class)) {
String value = field.getDeclaredAnnotation(MyAnnotation.class).value();
((MyClass) field).annotatedValue = value;
return bean;
}
}
return bean;
}
}
The above BeanPostProcessor will be called for every bean during the app start up. It will check all the fields of a given bean to see if the field is of type MyClass. If it is, it will extract the value from the annotation and set it in the annotatedValue field.
The problem with this approach is that you can use MyAnnotation on any property in any class. You cannot enforce the annotation to be used only on MyClass.

Can a CDI Bean's method return a managed Bean it created?

i have the following setup:
#Applicationscoped
#Transactional(txtype.Requires_new)
Public class querybean {
#Inject ExternalSysrltem externalSystemProxy;
Public Handle gethandleByKey(String key) {
return new Handle(/*do external Systems Query, returns an ExternalHandle Object*/)
}
Public static class Handle {
ExternalHandle eh;
/*protected so that User of class cannot Instantiate it otherwise that by getHandleByKey()*/
Protected Handle(ExternalHandle arg) {
This.eh = arg;
}
Public String getHandleInfo() {
Return This.eh.getName() + "/" + this.eh.getState()..;
/*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
}
}
}
Can I get Handle to be a Managed Bean that can be annotated with #Transactional and still create it in the getHandleByKey Method at Runtime by querying the external System?
A static inner class can be a bean according the the spec.
In your example it is not a bean due to its constructor.
As said in comments you could use a producer, but a produced bean can't be intercepted (with #Transaction here)
If you want to keep your pattern, you'll have to create a very complex extension since it should work at low level to ensure interceptor will be activated.
I suggest that you go for something simpler by deporting your ExternalHandle resolution in Handle Bean, allowing you to use a String to construct it.
First create a qualifier with a non binding member to transmit information to your constructor.
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RUNTIME)
#Documented
#Qualifier
public #interface Keyed {
#Nonbinding
String key();
}
Then create a literal for your annotation to allow creation of an annotation instance with a given key value.
public class KeyedLiteral extends AnnotationLiteral<Keyed> implements Keyed {
private final String key;
public KeyedLiteral(String key) {
this.key = key;
}
#Override
public String key() {
return key;
}
}
Using programmatic lookup and InjectionPoint to transmit your key value. Your code will be like:
#Applicationscoped
#Transactional(txtype.Requires_new)
Public class querybean {
#Inject
#Any
Instance<Handle> handles;
Public Handle gethandleByKey(String key) {
return instances.select(new KeyedLiteral(key)).get()
}
#Dependent
#Transactional
#Keyed("") //enforce the presence of the annotation for the constructor
Public static class Handle {
ExternalHandle eh;
// needed to make the bean proxyable (mandatory for the interceptor bound))
Protected Handle() {}
#Inject
Protected Handle(InjectionPoint ip, ExternalSysrltem externalSystem) {
String key=ip.getAnnotated().getAnnotation(Keyed.class).key();
eh = /*do external Systems Query, returns an ExternalHandle Object from key and externalSystem*/
}
Public String getHandleInfo() {
Return This.eh.getName() + "/" + this.eh.getState()..;
/*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
}
}
}

Invoke other method instead

I have two methods and one of them with an annotation, let's say:
#ReplacingMethod(bar)
public void foo() { ... }
public void bar { ... }
Is it possible to invoke bar instead of foo whenever foo is called, without jumping into the body of foo? I did some research on this and were not able to set a return value via reflections. Any suggestions?
You can achieve this using Aspect Oriented Programming, e.g. with Spring AOP. I don't think you can change method implementation in pure Java without AOP.
Let me give you an example how to achieve what you asked for with Spring AOP. First, define your annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReplacingMethod {
String value();
}
Then define an aspect that will do the actual replacing of method:
#Aspect // aspect is a module encapsulating your replacing functionality
public class ReplacingAspect {
// pointcut gives an expression selecting the "joint points" to be intercepted
#Pointcut("#annotation(example.annotation.ReplacingMethod)")
public void methodToBeReplaced() { }
// advice defining the code executed at joint points selected by given pointcut;
// in our case #Around is executed instead of the method call selected by pointcut methodToBeReplaced()
#Around("methodToBeReplaced()")
public void replaceMethodCall(ProceedingJoinPoint pjp) throws Throwable {
// get reference to the method to be replaced
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
// extract the name of the method to be called from ReplacingMethod annotation
ReplacingMethod replacingMethodAnnotation = method.getAnnotation(ReplacingMethod.class);
String methodToCallName = replacingMethodAnnotation.value();
// use reflection to call the method
Method methodToCall = pjp.getTarget().getClass().getMethod(methodToCallName);
methodToCall.invoke(pjp.getTarget());
}
}
Now, assuming you have class TestClass where you have applied your #ReplacingMethod annotation,
public class TestClass {
#ReplacingMethod("bar")
public void foo() { System.out.println("foo"); }
public void bar() { System.out.println("bar"); }
}
the last missing piece is to get create your instance of TestClass with AOP enabled and your ReplacingAspect applied:
public class Main {
public static void main(String... args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); // create Spring context that enables AOP under the hood
TestClass testObject = context.getBean(TestClass.class); // we get reference to TestClass instance from context; calling on a plain new instance wouldn't work
testObject.foo(); // prints "bar" !
}
#EnableAspectJAutoProxy // enables AOP support
#Configuration
public static class TestConfiguration {
#Bean public TestClass testClass() { return new TestClass(); }
#Bean public ReplacingAspect aspect() { return new ReplacingAspect(); } // enables our ReplacingAspect
}
}
You can check out the whole working example at GitHub.
Reflection cannot change the schema of a class and not its behaviour. It can only call (possibly hidden) features.
If you want to replace a method call by another try out a byte code library as asm or javassist. These tools allow you to change class definitions and behaviour (even at runtime with some restrictions).
The approach with AOP is easier, but it is not as flexible and its classpath footprint is larger.

Categories