i am using play framework , i need to check the user's permissions with #secure annotation , but i get a problem here :
#secure(UID=???)
public static void removeFavorite(Long storyId,Long userId){
}
can any one tell me how to pass the "userId" parameter to "UID" in the annotation ?
PS : the "userId" parameter is in request scope.
many thanks!
AFAIK you can't change annotations at runtime (at least not without dynamic code generation). Additionally, annotations are static, i.e. they apply to classes or class members (fields, methods etc.) and can't be changed per instance. Thus you can't pass the userId to that annotation.
I dont know what#securedoes, but generally, you'd read the annotation at runtime and optionally check its static parameters and if those checks succeed you'd read theuserId` parameter and do whatever is appropriate when that annotation is present.
Related
For my data objects in Kotlin I have added a custom annotation for GSON to have an exclusion rule.
In the past this has worked perfectly, now it does not show up in my class reflection (this.javaClass.declaredFields[3].annotations is null) nor does it show up in the compiled java output.
I have tried different things, like upgrading my kotlin version, adding kotlin-kapt, using different #Retention types, restarting my computer (you never know) and have looked at other annotations. Those other annotations (for instance for Hibernate a #OneToOne) shows up with no issue.
Annotation definition:
#Retention(AnnotationRetention.RUNTIME)
#Repeatable
#Target(
AnnotationTarget.FIELD,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.PROPERTY,
AnnotationTarget.VALUE_PARAMETER
)
annotation class ExcludeFromJSON
Usage in data class:
#Entity
#Table(name = "user")
class User (
var username: String = "",
var email: String = "",
#ExcludeFromJSON
private var password: String
) {}
I expect the annotation to show up in the javaClass reflection and in the compiled java code. It does neither.
Compiled password var (no annotation...):
private final var password: kotlin.String /* compiled code */`
You should qualify the annotation with the appropriate use-site target:
#field:ExcludeFromJSON
private var password: String
This will cause the annotation to be present on the Java field generated by this property.
From the Kotlin Reference regarding Annotation Use-site Targets:
When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. [...]
[...]
The full list of supported use-site targets is:
file;
property (annotations with this target are not visible to Java);
field;
get (property getter);
set (property setter);
receiver (receiver parameter of an extension function or property);
param (constructor parameter);
setparam (property setter parameter);
delegate (the field storing the delegate instance for a delegated property).
[...]
If you don't specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
param;
property;
field.
The three important things to take away from that are:
Annotations in Kotlin have a certain amount of ambiguity1 regarding where they're ultimately applied. For instance, placing your annotation where you did means said annotation could be applied to one of at least five different locations: property, field, getter, setter, setter parameter.
An annotation applied to a Kotlin property is not visible on the Java side.
An annotation on a Kotlin property, without a use-site target, will only be applied to the backing field if it's not also applicable to being applied to the Kotlin property.
Your annotation has both AnnotationTarget.FIELD and AnnotationTarget.PROPERTY in its #Target annotation. Since you don't specify a use-site target, the property takes precedence—meaning the annotation is not visible to Java.
For more information regarding properties and fields, see the Properties and Fields page of the Kotlin Reference.
1. It's not technically ambiguous, as everything is well defined.
How can I distinguish in java graphQL if a parameter was explicitly set to null, or if it was not provided at all?
The use case that I try to achieve is the following: I have a mutation like this
updateUser(id:Int!, changes:UserChanges!)
#where UserChanges is defined as:
type UserChanges {
login:String
email:String
#etc
}
The idea here is that the user provides only the fields that he wants to change (like react setState for example).
So if email is ommited, I want to leave it unchanged.
But what if he/she wants to explicitly set email to null?
Is there any way figure this out from my resolver method and act accordingly?
(I use graphql-java-tools library)
I found the answer. In case somebody needs it:
An instance of graphql.schema.DataFetchingEnvironment is available to every resolver method.
This provides methods like getArguments(), hasArgument() etc.
Using those methods, we can find out if an argument was an explicit set to null, or if it was not provided at all.
Looks like deserialization from query/variables is handled by fasterxml Jackson, and that's proper place to deal with the issue, otherwise it becomes too complex: check every field? nested?
So: UserChanges.java should look like this:
class UserChanges {
// SHOULD NOT HAVE ALL ARGUMENT CONSTRUCTOR!
Optional<String> login;
Optional<String> email;
... getters & setters
}
in this case deserializer will use setters, ONLY FOR PROVIDED FIELDS!
And {"login":null} will become:
UserChanges.login = Optional.empty
UserChanges.email = null
I have interface Resource and several classes implementing it, for example Audio, Video... Further, I have created custom annotation MyAnnotation with Class type param:
#MyAnnotation(type = Audio.class)
class Audio {
...
}
#MyAnnotation(type = Video.class)
class Video{
...
}
In some other place in code I have to use Interface Resource as a returned type:
public class Operations<T extends Resource> {
....
#OtherAnnotation(type = Audio.class (if audio), type = Video.class (if video) )
T getResource();
....
}
The question is how to appropriatelly annotate annotation #OtherAnnotation depending of what kind of Resource type will be returned ?
What you are asking is for dynamic values for annotation attributes.
However annotations can only be set at compile time which is the reason why their values can only be compile time constants. You may only read them at runtime.
There was a similar question in which someone tried to generate the annotation value , it's answer explains why there is no way to dynamically generate a value used in annotation in a bit more detail. In that question there was an attempt to use a final class variable generated with a static method.
There are annotation processors which offer a bit more flexibility by handling placeholders. However i don't think this fits your case, as you want the dynamic values at runtime.
This answer refers to spring's use of the expression language for the Value annotation in which the placeholder (#Value("#{systemProperties.dbName})") gets overrided with the data from one of the property sources defined ( example in spring boot )
In any case, you will have to rethink your architecture a bit.
I want to provide annotations with some values generated by some methods.
I tried this so far:
public #interface MyInterface {
String aString();
}
#MyInterface(aString = MyClass.GENERIC_GENERATED_NAME)
public class MyClass {
static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class);
public static final String generateName(final Class<?> c) {
return c.getClass().getName();
}
}
Thought GENERIC_GENERATED_NAME is static final, it complains that
The value for annotation attribute MyInterface.aString must be a constant expression
So how to achieve this ?
There is no way to dynamically generate a string used in an annotation. The compiler evaluates annotation metadata for RetentionPolicy.RUNTIME annotations at compile time, but GENERIC_GENERATED_NAME isn't known until runtime. And you can't use generated values for annotations that are RetentionPolicy.SOURCE because they are discarded after compile time, so those generated values would never be known.
The solution is to use an annotated method instead. Call that method (with reflection) to get the dynamic value.
From the user's perspective we'd have:
#MyInterface
public class MyClass {
#MyName
public String generateName() {
return MyClass.class.getName();
}
}
The annotation itself would be defined as
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface #MyName {
}
Implementing the lookup for both of these annotations is rather straight-forward.
// as looked up by #MyInterface
Class<?> clazz;
Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 1) {
// error
}
Method method = methods[0];
if (!method.isAnnotationPresent(MyName.class)) {
// error as well
}
// This works if the class has a public empty constructor
// (otherwise, get constructor & use setAccessible(true))
Object instance = clazz.newInstance();
// the dynamic value is here:
String name = (String) method.invoke(instance);
There is no way to modify the properties of an annotation dynamically like others said. Still if you want to achieve that, there are two ways to do this.
Assign an expression to the property in the annotation and process that expression whenever you retrieve the annotation. In your case your annotation can be
#MyInterface(aString = "objectA.doSomething(args1, args2)")
When you read that, you can process the string and make the method invocation and retrieve the value. Spring does that by SPEL (Spring expression language). This is resource intensive and the cpu cycles are wasted every time we want to process the expression. If you are using spring, you can hook in a beanPostProcessor and process the expression once and store the result somewhere. (Either a global properties object or in a map which can be retrieved anywhere).
This is a hacky way of doing what we want. Java stores a private variable which maintains a map of annotations on the class/field/method. You can use reflection and get hold of that map. So while processing the annotation for the first time, we resolve the expression and find the actual value. Then we create an annotation object of the required type. We can put the newly created annotation with the actual value (which is constant) on the property of the annotation and override the actual annotation in the retrieved map.
The way jdk stores the annotation map is java version dependent and is not reliable since it is not exposed for use (it is private).
You can find a reference implementation here.
https://rationaleemotions.wordpress.com/2016/05/27/changing-annotation-values-at-runtime/
P.S: I haven't tried and tested the second method.
I’m writing a maven plugin with a number of configurable parameters. There are a number of parameters specified in the Mojo class. One of these parameters is required and must contain certain values (let’s say, either ‘Atwood’ or ‘Spolsky’). At the moment it is annotated with a. #required field as shows here:
public class GenerateMojo extends AbstractMojo{
...
...
/**
*#parameter
*#required
*/
private String someParameter;
...
...
}
Which is all well and good, but if someone forgets to include the parameter they get a generic error message like so:
Inside the definition for plugin 'xyz' specify the following:
<configuration>
...
<someParameter>VALUE</someParameter>
</configuration>
If is possible to either (1) restrict the values that can be inputted to the someParmeter field to give a better error message, or (2) specify the error message myself so that I can write something like “The value for ‘someParameter’ needs to be either ‘Atwood’ or ‘Spolsky’ ??
Thanks
There is an open Jira to add support for enumerations to parameter values in Maven 2.2 (it is already supported in Plexus on Java 5).
You can specify a default value so at least the Mojo won't fail in initialisation. You can then validate the value of the parameter in the execute() method and output a more helpful message.
If there is no sensible default, you can set the default to a value that will be invalidated in the execute() method, this effectively means the user will have to define it, and they get a meaningful error message. For example:
/**
* #parameter expression="${someParameter}" default-value="_"
*/
private String someParameter;