Gradle plugin: cannot process well known annotations available from script classpath for classes loaded via URLClassLoader


(Davide Cavestro) #1

I’m facing a weird behavior trying to port the maven-swagger-plugin to gradle: the plugin code should process some well known annotations (available from gradle script classpath) and test if they are used on the classes contained within the project artifacts (loaded through a URLClassLoader). The problem is that calling ‘‘Class.getAnnotation (Foo)’’ on a class loaded with the ‘‘URLClassLoader’’ returns ‘‘null’’ as the annotation instance seems to be actually a Proxy instance.

Follows an example of a gradle script failing to load the ‘‘Foo’’ annotation

  buildscript {

repositories {

flatDir dirs: [’…/code/foo/build/libs’]

}

dependencies {

classpath (’:foo:’)

} }

import java.lang.annotation.Annotation

ClassLoader cl = new URLClassLoader([’…/code/foo/build/libs/foo.jar’, ‘…/code/bar/build/libs/bar.jar’].collect {file(it).toURI().toURL()}.toArray (new URL[0])); Class<?> barClass = cl.loadClass(“Bar”);

Annotation[] annotations = barClass.getAnnotations(); annotations.each {Annotation annotation->

logger.lifecycle ("Annotations contains: "+annotation.annotationType()); }

//def annotation = barClass.getAnnotation(cl.loadClass(“Foo”)) //would WORK def annotation = barClass.getAnnotation(Foo) //make the assertion FAIL assert annotation

The same code would work if the annotation class I’m searching for would be loaded through the ‘‘URLClassLoader’’. The problem is that the actual annotation check is implemented into a 3rd party library (from swagger) and should be executed by the gradle plugin. I’ve tried to share a test case at https://gist.github.com/davidecavestro/9911631 but due to gist limitations I had to flatten file paths (replacing slashes with underscores). However the original test case had this structure

  .  ├── code
  1. cd here and do a ‘gradle assemble’ │ ├── bar │ │ ├── build.gradle │ │ └── src │ │

└── main │ │

└── java │ │

└── Bar.java │ ├── build.gradle │ ├── foo │ │ ├── build.gradle │ │ └── src │ │

└── main │ │

└── java │ │

└── Foo.java │ └── settings.gradle ├── failing

  1. cd here and simply run ‘gradle’: it fails │ └── build.gradle └── working

  2. cd here and simply run ‘gradle’: it works

└── build.gradle


(Peter Niederwieser) #2

This doesn’t look like a Gradle related problem to me. Classes loaded via different class loaders will never be considered the same, hence the assertion is expected to fail. Proxies are how annotation classes are implemented in the JDK. Using class loaders to perform code analysis is generally problematic; a better approach is to use a library such as ASM.


(Davide Cavestro) #3

I see. I’m going to propose swagger guys to replace the code analysis logic with another implementation possibly based on ASM. Is there in your opinion any workaround I could apply in the meantime, such as loading/running gradle plugin code through that URLClassloader?


(Peter Niederwieser) #4

You’ll have to load everything from the same class loader, whichever class loader that is.


(Davide Cavestro) #5

ok, so invoking ‘‘new URLClassLoader(urls, getClass().getClassLoader())’’ from the gradle plugin is enough to make it share the same annotation class instance with the code analysis logic (reflection based). Many thanks