I develop simple unit test library, just for fun and experience. As long as I write unit tests, I follow given-when-then or arrange-act-assert pattern. So I thought, that instead of write this pattern names as single line comments in test method body:
#TestClass(enabled = false)
public class DemonstrationTest {
#UnitTest
public void StringBuilderSuccess() throws AssertFailureException{
//Given
String firstTitle = "CosmicWhale";
String secondTitle = "AmbientTurtle";
StringBuilder stringBuilder = new StringBuilder();
String expected = firstTitle + " " + secondTitle;
//When
String actual = stringBuilder.append(firstTitle).append(" ").append(secondTitle).toString();
//Then
btester.framework.Assert.assertEquals(expected, actual);
}
}
I can create annotations, to follow this pattern. This is much more elegant:
#UnitTest
public void StringBuilderSuccess() throws AssertFailureException{
#Given
String firstTitle = "CosmicWhale";
String secondTitle = "AmbientTurtle";
StringBuilder stringBuilder = new StringBuilder();
String expected = firstTitle + " " + secondTitle;
#When
String actual = stringBuilder.append(firstTitle).append(" ").append(secondTitle).toString();
#Then
String localVariable;
btester.framework.Assert.assertEquals(expected, actual);
}
My problem is, that each of these annotations, must be above some local variable. This is not a problem with #Given and #When annotations, but it is a big problem with the last, #Then annotation. I've created redundand local variable under #Then annotation, to bypass the problem.
Question: Is there any chance to place annotation above Assert in the code?
Each annotation body looks lika this:
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.LOCAL_VARIABLE)
public #interface When {
}
I'm using Java 8.
I will be grateful for any help.
Ps. If you want to check or contribute my project, you can found it here.
Java 8 does not permit writing annotations on statements.
It was considered, but not included in Java 8.
Here is some background.
Java 5+ permits writing annotations on declarations.
Java 8+ permits writing annotations on types.
The Java 8 feature for writing annotations on types was known by its code name "JSR 308". Java 8 made some other extensions to Java annotations, such as permitting duplicate annotations, and representing declaration annotations on local variables in the classfile. JSR 308 also considered other extensions to annotations, including annotations on statements. Although annotations on statements have a number of use cases, in the end JSR 308, and therefore Java 8, did not include the annotations-on-statements feature.
As far as i know : annotation can be applied to methods only when they are being declared, not when being called.
I dont see any clear use of annotations for above code. but, if you really want to just have annotations
wrap the assertion statement with a method which returns non void data type. (probably boolean ?)
in above implementation an annotation will be applied to return value of method. ( you may choose to store it with a variable name)
Related
I'm trying to dynamically load a java class. The basic idea is, that a jar contains modules which get loaded dynamically at runtime. This is how I do it (I know it's hacky, but there is no other method to dynamically add a jar to an already existing classloader afaik):
Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });
method.setAccessible(true);
method.invoke(moduleLoader, new Object[] { file.toURI().toURL() });
Class fooClass = moduleLoader.loadClass("com.coderunner.Foo");
Object foo = fooClass.newInstance();
Every module is annotated with an #Module annotation. So in order to gain further informations about the module, I try to get the annotation. The problem is that the annotation on foo is of type com.sun.$Proxy$27 instead of com.coderunner.Module and therefore I get a
ClassCastException: Cannot cast com.sun.proxy.$Proxy42 (id=64) to com.coderunner.Module
I have to say I'm a bit confused what happens here. Is what I want to do possible? How?
Edit: I maybe also should mention I'm trying this in a spring/spring-mvc and tomcat environment.
The fact that the reflection returns a proxy object does not prevent you from gathering information about the annotation and its values.
The getclass method returns a proxy object:
log.info("annotation class:" + annotation.getClass());
Output:
[INFO] annotation class:class com.sun.proxy.$Proxy16class
The output is that same as in your example, but that is no problem. Having the method (or field) is enough. The additional part is to just invoke the annotation method.
public void analyseClass(Class myClass) {
for (Method method: myClass.getMethods()) {
System.out.println("aanotations :" + Arrays.toString(field.getAnnotations()));
for (Annotation annotation : method.getAnnotations()) {
log.info("annotation class:" + annotation.getClass());
log.info("annotation class type:" + annotation.annotationType());
Class<Annotation> type = (Class<Annotation>) annotation.annotationType();
/* extract info only for a certain annotation */
if(type.getName().equals(MyAnnotation.class.getName())) {
String annotationValue =
(String) type.getMethod("MY_ANNOTATION_CERTAIN_METHOD_NAME").invoke(annotation);
log.info("annotationValue :" + annotationValue);
break;
}
}
}
//do the same for the fields of the class
for (Field field : myClass.getFields()) {
//...
}
}
To come to this solution, I used the following post:
How to get annotation class name, attribute values using reflection
The fact that you get a proxy in front of your annotation type should not matter. It might actually mislead you into believing that this is the cause for problems you are having. If stuff like "isAnnotationPresent(..)" fails, it is not due to that proxy, it is because you have loaded the annotation class multiple times using multiple classloaders. For example, Jetty gives priority to the WebApp classloader by default. So if your Jetty server instance (or Tomcat or whatever) already has loaded the annotation class, and the annotation is on your WebApp's classpath, too, you can have problems like "getAnnotation()" not returning anything. Just make sure that the library containing your annotation is not loaded twice.
The solution provided by Andreas is, well, a very dirty workaround and just covers up the fact that you probably don't have your classloading under control/properly organized.
I had the same problem when trying to create a ant task for code generation based on a declarative approach using annotations.
I found that the documentation of the Proxy - Object states that instanceof should resolve it,
but this didn't work fopr me neither.
I finally got a around with
Annotation[] annotations = classObj.getAnnotations();
for(int i = 0;i < annotations.length;i++) {
Class<?>[] interfaces = annotations[i].getClass().getInterfaces();
for(int i2 = 0; i2 < interfaces.length;i2++) {
System.out.println("interface:" + interfaces[i2].getName());
}
giving my the name of the original annotation, so comparing this name to the annotations classname will give you the desired result.
Given a Google Cloud Endpoints project in Eclipse with the servlet-class annotated with #Api(name="helloworld"), the Endpoints framework generates a file named war/WEB-INF/helloworld-v1.api when the project compiles successfully. Sometimes this file is not generated even if there are no compilation errors though - only what I will call "GAE Endpoints code convention errors".
Example - working:
public class TestEntity {
public String Text;
public TestEntity(String text){
Text = text;
}
}
#ApiMethod
public TestEntity getTestEntity(){
return new TestEntity("Hello world");
}
Example - NOT working:
// The TestEntity-class is unchanged
#ApiMethod
public TestEntity getTestEntity(String input){
return new TestEntity("Hello world");
}
The problem with the latter example is that I take a String parameter as input without annotating it with #Named. I know that in this example, but there might be other cases where this is not so obvious.
Is there anywhere where I can read some sort of error log on why the .api file is not generated?
Although I am a fan of code by convention, it really takes the programming efficiency a step back if I cannot get feedback on what I do wrong. Eclipse provides compiler error feedback. The Google Cloud Endpoints Framework should provide Code-By-Convention-Rule-Breaking feedback.
There isn't currently good logging or error messaging when code generation fails, though it's one of the (if not most) requested features. In the interim, here's a list of the common failure cases:
The return type is invalid. Return types must be objects conforming to JavaBean conventions, and types like Object, String, and Integer are not allowed.
One or more argument types are invalid. Methods may accept at most one object in the POST body, and this object should also conform to JavaBean conventions. Methods may accept zero or more arguments via the query string (using the #Named annotation) and these must be scalar types (e.g. String, Integer).
An API, method, or parameter has an invalid name. APIs, methods, and parameters should be named to match the following regular expression: [a-z]+[A-Za-z0-9]*. Convention also suggests using lowerCamelCase for naming (though alllowercase is allowed).
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.
Update: Oracle has confirmed this as a bug.
Summary: Certain custom BeanInfos and PropertyDescriptors that work in JDK 1.6 fail in JDK 1.7, and some only fail after Garbage Collection has run and cleared certain SoftReferences.
Edit: This will also break the ExtendedBeanInfo in Spring 3.1 as noted at the bottom of the post.
Edit: If you invoke sections 7.1 or 8.3 of the JavaBeans spec, explain
exactly where those parts of the spec require anything. The
language is not imperative or normative in those sections. The
language in those sections is that of examples, which are at best
ambiguous as a specification. Furthermore, the BeanInfo API
specifically allows one to change the default behavior, and it is
clearly broken in the second example below.
The Java Beans specification looks for default setter methods with a void return type, but it allows customization of the getter and setter methods through a java.beans.PropertyDescriptor. The simplest way to use it has been to specify the names of the getter and setter.
new PropertyDescriptor("foo", MyClass.class, "getFoo", "setFoo");
This has worked in JDK 1.5 and JDK 1.6 to specify the setter name even when its return type is not void as in the test case below:
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import org.testng.annotations.*;
/**
* Shows what has worked up until JDK 1.7.
*/
public class PropertyDescriptorTest
{
private int i;
public int getI() { return i; }
// A setter that my people call "fluent".
public PropertyDescriptorTest setI(final int i) { this.i = i; return this; }
#Test
public void fluentBeans() throws IntrospectionException
{
// This throws an exception only in JDK 1.7.
final PropertyDescriptor pd = new PropertyDescriptor("i",
PropertyDescriptorTest.class, "getI", "setI");
assert pd.getReadMethod() != null;
assert pd.getWriteMethod() != null;
}
}
The example of custom BeanInfos, which allow the programmatic control of PropertyDescriptors in the Java Beans specification all use void return types for their setters, but nothing in the specification indicates that those examples are normative, and now the behavior of this low-level utility has changed in the new Java classes, which happens to have broken some code on which I am working.
There are numerous changes in in the java.beans package between JDK 1.6 and 1.7, but the one that causes this test to fail appears to be in this diff:
## -240,11 +289,16 ##
}
if (writeMethodName == null) {
- writeMethodName = "set" + getBaseName();
+ writeMethodName = Introspector.SET_PREFIX + getBaseName();
}
- writeMethod = Introspector.findMethod(cls, writeMethodName, 1,
- (type == null) ? null : new Class[] { type });
+ Class[] args = (type == null) ? null : new Class[] { type };
+ writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args);
+ if (writeMethod != null) {
+ if (!writeMethod.getReturnType().equals(void.class)) {
+ writeMethod = null;
+ }
+ }
try {
setWriteMethod(writeMethod);
} catch (IntrospectionException ex) {
Instead of simply accepting the method with the correct name and parameters, the PropertyDescriptor is now also checking the return type to see whether it is null, so the fluent setter no longer gets used. The PropertyDescriptor throws an IntrospectionException in this case: "Method not found: setI".
However, the problem is much more insidious than the simple test above. Another way to specify the getter and setter methods in the PropertyDescriptor for a custom BeanInfo is to use the actual Method objects:
#Test
public void fluentBeansByMethod()
throws IntrospectionException, NoSuchMethodException
{
final Method readMethod = PropertyDescriptorTest.class.getMethod("getI");
final Method writeMethod = PropertyDescriptorTest.class.getMethod("setI",
Integer.TYPE);
final PropertyDescriptor pd = new PropertyDescriptor("i", readMethod,
writeMethod);
assert pd.getReadMethod() != null;
assert pd.getWriteMethod() != null;
}
Now the above code will pass a unit test in both 1.6 and in 1.7, but the code will begin to fail at some point in time during the life of the JVM instance owing to the very same change that causes the first example to fail immediately. In the second example the only indication that anything has gone wrong comes when trying to use the custom PropertyDescriptor. The setter is null, and most utility code takes that to mean that the property is read-only.
The code in the diff is inside PropertyDescriptor.getWriteMethod(). It executes when the SoftReference holding the actual setter Method is empty. This code is invoked by the PropertyDescriptor constructor in the first example that takes the accessor method names above because initially there is no Method saved in the SoftReferences holding the actual getter and setter.
In the second example the read method and write method are stored in SoftReference objects in the PropertyDescriptor by the constructor, and at first these will contain references to the readMethod and writeMethod getter and setter Methods given to the constructor. If at some point those Soft references are cleared as the Garbage Collector is allowed to do (and it will do), then the getWriteMethod() code will see that the SoftReference gives back null, and it will try to discover the setter. This time, using the same code path inside PropertyDescriptor that causes the first example to fail in JDK 1.7, it will set the write Method to null because the return type is not void. (The return type is not part of a Java method signature.)
Having the behavior change like this over time when using a custom BeanInfo can be extremely confusing. Trying to duplicate the conditions that cause the Garbage Collector to clear those particular SoftReferences is also tedious (though maybe some instrumenting mocking may help.)
The Spring ExtendedBeanInfo class has tests similar to those above. Here is an actual Spring 3.1.1 unit test from ExtendedBeanInfoTest that will pass in unit test mode, but the code being tested will fail in the post-GC insidious mode::
#Test
public void nonStandardWriteMethodOnly() throws IntrospectionException {
#SuppressWarnings("unused") class C {
public C setFoo(String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
One suggestion is that we can keep the current code working with the non-void setters by preventing the setter methods from being only softly reachable. That seems like it would work, but that is rather a hack around the changed behavior in JDK 1.7.
Q: Is there some definitive specification stating that non-void setters should be anathema? I've found nothing, and I currently consider this a bug in the JDK 1.7 libraries.
Am I wrong, and why?
Looks like the specification hasn't changed (it requires void setter) but the implementation has been updated to only allow void setters.
Specification:
http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html
More specifically see section 7.1 (accessor methods) and 8.3 (design patterns for simple properties)
See some of the later answers in this stackoverflow question:
Does Java bean's setter permit return this?
Section 8.2 specifies:
However, within Java Beans the use of method and type names that match design patterns is entirely optional. If a programmer is prepared to explicitly specify their properties, methods, and events using the BeanInfo interface then they can call their methods and types whatever they like. However, these methods and types will still have to match the required type signatures, as this is essential to their operation.
(emphasis added)
Also, I beleive the method signatures shown in 7.1 and 8.3 are, in fact, normative. They are examples only in the sense that they use "foo" as an example property name.
I would also opt to say disallowing non-void setters is an error. It simply makes fluent programming impossible. Thats why it needs to be be changed.
Since I found Spring 3.1.1 ExtendedBeanInfo unit tests that expect the code not to be broken, and because having the behavior change after garbage collection is obviously a bug, I shall answer this and note the Java bug numbers. The bugs are still not visible on the external database of Java bugs, but I hope that they will become visible at some point:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7172854 (Oracle closed this as a duplicate of the bug below since they have the same cause despite different manifestations.)
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7172865
(The bugs were submitted on May 30, 2012.)
As of June 20, 2012, the bugs are visible in the external database via the links above.
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";
}
}