I have this class with defined constants
public class Constants {
public static final boolean TEST = true;
}
I would like to make a check is this constant TRUE like this:
#Test
#EnabledIf("'true' == Constants.TEST")
public void theMeasurementSampleScreenFromPickConfirm() throws InterruptedException {
// some code execution
}
But I get error java.lang.NoSuchMethodError: 'java.lang.reflect.Method org.junit.platform.commons.util.ReflectionUtils.getRequiredMethod(java.lang.Class, java.lang.String, java.lang.Class[])'
Do you know how I can implement this check properly.
Don't use single quote as you are comparing a boolean value.
Solution 1: Append the value in the annotation itself.
#Test
#EnabledIf("#{" + Constants.TEST + "}")
public void theMeasurementSampleScreenFromPickConfirm() throws InterruptedException {
// some code execution
}
Solution 2 : Using Spring Expression Language, you can use Type operator by providing the fully qualified name of the class. For example : If the Constants class is in the com.example package, then
#Test
#EnabledIf("#{T(com.example.Constants).TEST}")
public void theMeasurementSampleScreenFromPickConfirm() throws InterruptedException {
// some code execution
}
Note: This will only work for Spring projects with Spring Boot Starter Test (internally uses JUnit by default) support as SpEL cannot be evaluated if it's non-spring project. JUnit doesn't support the evaluation of SpEL without Spring independently.
So, create a spring boot project with spring-boot-starter-test and use #EnabledIf annotation from spring-boot-starter-test which is capable of evaluating Spring Expression Language.
When using Spring's #EnabledIf, you can use a SpEL expresion. See #EnabledIf With a SpEL Expression
To reference a constant, use the T operator from SpEL
You can use the special T operator to specify an instance of java.lang.Class (the type). Static methods are invoked by using this operator as well
Also note that your TEST constant is a boolean, not a String.
Combining the above you can use:
#EnabledIf("#{T(com.sandbox.Constants).TEST == true}")
or even
#EnabledIf("#{T(com.sandbox.Constants).TEST}")
Related
Question is : how can I handle a chain of fallback values in Spring expression, allowing me to fallback on higher level configuration until I got a defined value ?
To explain a little, let's illustrate this with my own use case : I was intending to create a Spring application using the #Scheduled annotation to run some processes. Thing is I have many scheduled tasks and I would let the running frequency to be easily configurable for all of them, or only for a subset.
So I was looking for something like
#Component
public class SpecificTaskRunner {
#Scheduled(cron = "${ specific-task-cron ?: task-families-cron ?: default-cron }")
public void specificTask() {
doSomething();
}
}
Letting application configure either the frequency of all the Scheduled task at once by overriding the default-cron value, or only a subset of them by overriding the task family property, or finally, specifying on a task basis. Advantage of this approach is that it allows to play with multiple configuration levels as every scheduled task looks for the appropriate property starting from the most specific, and looking for a more generic until it got something defined, ultimately fall-backing on a default global value.
Unfortunately... this do not work. When evaluated, if the first element is not defined, then if fallback on the whole remaining. In this example, it would mean that if configuration misses a value for specific-task-cron, then the resolved value is : task-families-cron ?: default-cron which unfortunately is not what I'm looking for!
I found 2 way to handle it :
The first one, is purely based on Spring expression. Spring seems to re-evaluates the result of the Elvis operator if it also is a Spring expression. So my working configuration was :
#Component
public class SpecificTaskRunner {
#Scheduled(cron = "${ specific-task-cron ?: ${ task-families-cron ?: default-cron }}")
public void specificTask() {
doSomething();
}
}
Which in my real use case, with one more fallback, was not really convenient...
So I switched to a simpler solution that relies on configuration by defining a chain of values this way :
#application.yml
default-cron: '0 */5 0 0 0 0' #every 5 minutes
task-families-cron: ${default-cron}
specific-task-cron: ${task-families-cron}
Combined with
#Component
public class SpecificTaskRunner {
#Scheduled(cron = "${specific-task-cron}")
public void specificTask() {
doSomething();
}
}
This way any override of a property apply to hierarchical sub-levels, unless they got themselves overridden.
Both solutions seems to works, so at the end, yes Spring expression language handle multiple fallback. Whether it should be used, or is the configuration approach better readable... I let you decide.
In spring, it express the arg-names like this :
#Before(
value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && #annotation(auditable)",
argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code, bean, and jp
}
In the doc , it says you may leave out the name of the parameter from the value of the "argNames" attribute. How can spring get argNames("bean,auditable") from anyPublicMethod?
Did java provide some api to get the parameter names?
I think it use the aspectJ to parse the expression, did aspectJ provide the feature?
In Java 8 we can use reflection to get parameter name, see http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html. Though argument names will be present if the classes have been compiled with -g:vars.
In earlier versions we need to use some tool, like Javassist:
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass c = pool.get("test.Test");
CtMethod m = c.getDeclaredMethod("main");
MethodInfo methodInfo = m.getMethodInfo();
LocalVariableAttribute t = (LocalVariableAttribute) methodInfo.getCodeAttribute().getAttribute(javassist.bytecode.LocalVariableAttribute.tag);
int i = t.nameIndex(0);
String v = methodInfo.getConstPool().getUtf8Info(i);
System.out.println(v);
}
prints
args
Your annotation
#Before(
value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && #annotation(auditable)",
argNames="bean,auditable")
, assuming anyPublicMethod() matches any public method, will match any public method annotated with whatever type a parameter named auditable is and invoked on an object of bound to the parameter named bean.
In your method, a parameter named auditable is of type Auditable, so this #Before advice would match something like
public class SomeComponent {
#Auditable
public void someMethod() {}
}
I will bind the SomeComponent type bean to the bean parameter and the instance for the #Auditable annotation to the auditable parameter when invoking the advice.
Java does not have an API for getting parameter names. Parameter names are also not always present in the byte code. When they are, Spring uses the ASM library to parse the bytecode and retrieve them.
Just a complement to the other answers. Spring documentation is explicit : Spring does its best to retrieve correct parameters :
if using Java 8 it uses Java 8 reflection
if classes are compiled in debug mode, parameter name is present and spring uses it, same if ajc (aspectJ compiler) has been used)
it can eventually uses #Param annotations
it uses the type if not ambiguous
I'm using spring's PreAuthorize annotation as follows:
#PreAuthorize("hasRole('role')");
However, I already have 'role' defined as a static String on another class. If I try to use this value:
#PreAuthorize("hasRole(OtherClass.ROLE)");
I get an error:
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 14): Field or property 'OtherClass' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot'
Is there a way to access static variables like this with a PreAuthorize annotation?
Try the following which uses Spring Expression Language to evaluate the type:
#PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");
Be sure to specify the fully qualified class name.
Documentation
You can also create a bean container with roles, like:
#Component("R")
public final class RoleContainer {
public static final String ROLE_A = "ROLE_A";
}
then on controller you can use:
#PreAuthorize("hasRole(#R.ROLE_A)")
To make it possible to write expressions without package names:
<sec:global-method-security>
<sec:expression-handler ref="methodSecurityExpressionHandler"/>
</sec:global-method-security>
<bean id="methodSecurityExpressionHandler" class="my.example.DefaultMethodSecurityExpressionHandler"/>
Then extend the DefaultMethodSecurityExpressionHandler:
public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {
#Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport("my.example");
return standardEvaluationContext;
}
}
Now create my.example.Roles.java :
public class Roles {
public static final String ROLE_UNAUTHENTICATED = "ROLE_UNAUTHENTICATED";
public static final String ROLE_AUTHENTICATED = "ROLE_AUTHENTICATED";
}
And refer to it without package name in annotations:
#PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATED)")
instead of:
#PreAuthorize("hasRole(T(my.example.Roles).ROLE_AUTHENTICATED)")
Makes it more readable imho. Also roles are now typed. Write:
#PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATEDDDD)")
and you will get startup errors that wouldn't have been there if you wrote:
#PreAuthorize("hasRole('ROLE_AUTHENTICATEDDDD')")
Try something like this:
#PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");
If your OtherClass enum is declared as public static, then you need to use $ sign:
#PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");
name() to prevent futer problems if toString() will be overriden later
The accepted answer from Kevin Bowersox works, but I didn't like having the T(fully.qualified.path) stuff so I kept looking. I started by creating a custom security method using the answer from James Watkins here:
How to create custom methods for use in spring security expression language annotations
However, instead of a String, I used my enums.Permissions class as the parameter type:
#Component
public class MySecurityService {
public boolean hasPermission(enums.Permissions permission) {
...do some work here...
return true;
}
}
Now the neat part is that when I call the hasPermission from an annotation, I don't have to have to type the whole path, but I do have to enclose it in single quotes:
#PreAuthorize("#mySecurityService.hasPermission('SOME_ROLE_NAME')")
Because the hasPermission method expects an Enum, it will automatically find the Enum value with that name. If it doesn't find it you'll get an exception:
org.springframework.expression.spel.SpelEvaluationException: Type conversion problem, cannot convert from java.lang.String to enums.Permissions
You can rename hasPermission to hasRole, in which case the only trade off is that you are trading T(fully.qualified.path) for #mySecurityService and extra single quotes.
Not sure if it is any better, but there it is. Since none of this is going to verify the values at compile time anyways, my next step is to make an annotation processor.
I also have to give credit to krosenvold for pointing out that spring can automatically convert to an enum:
https://stackoverflow.com/a/516899/618881
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 want to be able to do this:
#Controller
#RequestMapping("/#{handlerMappingPaths.security}/*")
public class SecurityController {
etc
//for instance, to resuse the value as a base for the folder resolution
#Value("#{handlerMappingPaths.security}/")
public String RESOURCE_FOLDER;
#RequestMapping(value="/signin-again", method = RequestMethod.GET)
public String signinAgainHandler() {
return RESOURCE_FOLDER + "signin_again";
}
}
this doesn't appear to work now, am I missing something?
One way you can find out things like this is to have a look yourself. This is an example for eclipse, but it should work similarly for other IDEs:
First of all, make sure you have the sources of the spring libraries you are using. This is easiest if you use maven, using the maven-eclipse-plugin or using m2eclipse.
Then, in Eclipse select Navigate -> Open Type.... Enter the type you are looking for (something like RequestMa* should do for lazy typers like myself). Enter / OK. Now right-click the class name in the source file and select References -> Project. In the search view, all uses of this class or annotation will appear.
One of them is DefaultAnnotationHandlerMapping.determineUrlsForHandlerMethods(Class, boolean), where this code snippet will tell you that expression language is not evaluated:
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
RequestMapping mapping = AnnotationUtils.findAnnotation(
method, RequestMapping.class);
if (mapping != null) {
String[] mappedPatterns = mapping.value();
if (mappedPatterns.length > 0) {
for (String mappedPattern : mappedPatterns) {
// this is where Expression Language would be parsed
// but it isn't, as you can see
if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
mappedPattern = "/" + mappedPattern;
}
addUrlsForPath(urls, mappedPattern);
}
}
else if (hasTypeLevelMapping) {
urls.add(null);
}
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
Remember, it's called Open Source. There's no point in using Open Source Software if you don't try to understand what you are using.
Answering in 2020: with current Spring versions, SpEL expressions can be used in #RquestMappning annotations.
They are correctly parsed.
Inner details:
Spring's RequestMappingHandlerMapping calls embeddedValueResolver#resolveStringValue.
JavaDoc of EmbeddedValueResolver states the following:
StringValueResolver adapter for resolving placeholders and expressions
against a ConfigurableBeanFactory. Note that this adapter resolves
expressions as well, in contrast to the
ConfigurableBeanFactory.resolveEmbeddedValue method. The
BeanExpressionContext used is for the plain bean factory, with no
scope specified for any contextual objects to access.
Since: 4.3
This means both regular placeholders (e.g. ${my.property}) and SpEL expressions will be parsed.
Note that because regular placeholders are parsed first and SpEL expressions are parsed later, it's even possible to set the value of a property to a SpEL expression. Spring will first replace the placeholder with the property value (SpEL expression) and then parse the SpEL expression.
#Sean answered the question of whether spring supported this, but I also wanted to answer the question of just generally how not to duplicate configuration when using annotations. Turns out this is possible using static imports, as in:
import static com.test.util.RequestMappingConstants.SECURITY_CONTROLLER_PATH
#Controller
#RequestMapping("/" + SECURITY_CONTROLLER_PATH + "/*")
public class SecurityController {
etc
//for instance, to resuse the value as a base for the folder resolution
public String RESOURCE_FOLDER = SECURITY_CONTROLLER_PATH + "/";
#RequestMapping(value="/signin-again", method = RequestMethod.GET)
public String signinAgainHandler() {
return RESOURCE_FOLDER + "signin_again";
}
}