Gradle plugin with reflections


(Sergey) #1

Hello.
I want to do a Gradle task on Java that will look for classes marked an annotation in my Java application and process those classes. This my task method:

@TaskAction
public void greet() throws {

Reflections reflections = new Reflections(“ru.mycomp”);

for (Class<?> clazz : reflections.getTypesAnnotatedWith(MisEntity2.class)) {
System.out.println(clazz.toString());
}
}
But the code find the clasess in Gradle project, not in my application.
Any ideas…
Thanks.


(uklance) #2

The classpath for tasks is defined by the buildscript { classpath { ... } }.

I’m guessing you’ll want to create a UrlClassLoader based on configurations.runtime.files and pass that to Reflections


(Sergey) #3

Thank you very much Lance for your answer. Could you answer more in detail with example, because I am beginner.


(uklance) #4
@TaskAction
public void greet() {
    Set<File> runtimeFiles = project.configurations.runtime.files
    URL[] urls = runtimeFiles*.toUri().toUrl()
    ClassLoader classLoader = new java.net.URLClassLoader(urls)
    Configuration config = new ConfigurationBuilder("ru.mycomp", classLoader)
    Reflections reflections = new Reflections(config)
    for (Class<?> clazz : reflections.getTypesAnnotatedWith(MisEntity2.class)) {
        ...
    }
}

(Sergey) #5

Thanks a lot. Tomorrow I will try. When I have results, I will post them.


(Sergey) #6

This is my function which I made from answer above:

private void scanAnnotatedFields2() throws MalformedURLException {
        URL[] urls;
        List<URL> listOfURL = new ArrayList<>();
        for (Configuration configuration:  getProject().getConfigurations() ){
            System.out.println(configuration.getName());
        }
        System.out.println("---");
        Set<File> runtimeFiles = getProject().getConfigurations().findByName("runtime").getFiles();
        for(File file : runtimeFiles){
            listOfURL.add(file.toURI().toURL());
            System.out.println(file.getPath());
        }
        urls = listOfURL.toArray(new URL[0]);

        ClassLoader classLoader = new java.net.URLClassLoader(urls);

        Reflections reflections = new Reflections("ru.spsr", classLoader);

        for (Class<?> clazz : reflections.getTypesAnnotatedWith(MisEntity3.class)) {
            System.out.println(clazz.toString());
        }
    }

It don’t work. You advised “The classpath for tasks was defined by the buildscript { classpath { … } }.”. I have in this section reference on gradle plugin. Perhaps this is problem?


(uklance) #7

You’ll need to wire your task into Gradle’s DAG so that it runs after JavaCompile. Eg:

task myTask(type: MyReflectionsTask) {
   dependsOn compileJava
   ...
}

You’ll obviously need reflections on the buildscript.depencies.classpath (I’m not sure if this task is from buildSrc or a plugin). You won’t need the scanned classes on the buildscript classpath because we are using UrlClassloader to avoid a chicken-or-egg scenario.


(Sergey) #8

This is wiring:

public void apply(Project project) {
                Task task = project.getTasks().create("mis", Task.class);
                org.gradle.api.Task javaCompile = project.getTasks().getByName("compileJava");
                org.gradle.api.Task build = project.getTasks().getByName("build");
                //build.dependsOn(task);
                task.dependsOn(javaCompile);
            }    

This is classpath:

buildscript {
	repositories {
		mavenLocal()
		mavenCentral()
	}
	dependencies {
		classpath 'ru.spsr:mis-template-plugin:1.0.0-SNAPSHOT'
		classpath 'org.reflections:reflections:0.9.11'
	}
}

It doesn’t work :frowning:
Application shows the classes of plugin project… :frowning:


(uklance) #9

You might want to use the following so reflections doesn’t scan the buildscript classloader

new UrlClassloader(urls, null) 

You can check that the classloader is configured properly by attempting:

classLoader.loadClass("foo.bar.MyClass")

If that’s able to load one of the classes you are scanning for, I think you’re best to head over to the reflections forum as this is no longer a gradle question


(Sergey) #10

java.lang.ClassNotFoundException: ru.spsr.Request - The class is that I am scanning for in my application. :frowning:


(uklance) #11

Perhaps I’ve misunderstood configurations.runtime. Please add this before creating the URL array

listOfURL.add(project.sourceSets.main.output.classesDir.toUri().toUrl())

In java code this is a little different than groovy. I think you’ll need to do

SourceSetContainer ssc = getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
File classesDir = ssc.getByName("main").getOutput().getClassesDir();
listOfURL.add(classesDir.toUri().toUrl());

(Sergey) #12

Yes!!!
It works perfectly.
How can I thank you?


(SubratMohapatra) #13

Hi I am new to gradle.
I intend to use reflection as part of the gradle task to read all the classes in one of the packages in my multi-module repo. Need some help as I end up getting the following error:

Execution failed for task ‘:hello’.

No signature of method: java.io.File.toUri() is applicable for argument types: () values: []
Possible solutions: toURI(), toURL(), mkdir(), toString(), toPath(), toString()

######### Task Snippet

class AutogenTask extends DefaultTask {
@TaskAction
def greet() {

URL[] urls;
List<URL> listOfURL = new ArrayList<>();
SourceSetContainer ssc = getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
File classesDir = ssc.getByName("dir_name").getOutput().getClassesDir();
listOfURL.add(classesDir.toUri().toUrl());
urls = listOfURL.toArray(new URL[0]);

ClassLoader classLoader = new java.net.URLClassLoader(urls,null);

Reflections reflections = new Reflections("com.autogen.sample.common", classLoader);
Set<Class<? extends Object>> allClasses =
    reflections.getSubTypesOf(Object.class)
println "I am here2"
for(Class c:allClasses) {
  println "I am here3"
  println "Derived Class : " + c.getName()
}

}
}

task hello(type: AutogenTask)


(uklance) #14

Have you read the error message? It’s being pretty specific

Change:
classesDir.toUri()
To
classesDir.toURI()


(SubratMohapatra) #16

This is not throwing errors but it is also not getting the class names as it is intended here…
All the .java classes are under : com.autogenerate.data.common

Code Snippet

class AutogenTask extends DefaultTask {
@TaskAction
def greet() {
List listOfURL = new ArrayList<>();
File classesDir = project.file(“auto/src/generatedData/java/com/autogenerate/data/common”)
listOfURL.add(classesDir.toURI().toURL());
URL[] urls = listOfURL.toArray(new URL[0]);
ClassLoader classLoader = new URLClassLoader(urls);

Reflections reflections = new Reflections("com.autogenerate.data.common", classLoader);
Set<Class<? extends Object>> allClasses =
    reflections.getSubTypesOf(Object.class)
println "I am here2"
for(Class c:allClasses) {
  println "I am here3"
  println "Derived Class : " + c.getName()
}

}
}


Even if I go the route of SourceSetGenerator, I am still not able to grab the classes, where the package is in : auto ->java->com.autogenerate.data.common

class AutogenTask extends DefaultTask {
@TaskAction
def greet() {
SourceSetContainer ssc = getProject().findProject(“urns”).getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
File dir = ssc.getByName(“auto”).getOutput().getClassesDir();
List listOfURL = new ArrayList<>();
listOfURL.add(dir.toURI().toURL());
URL[] urls = listOfURL.toArray(new URL[0]);

ClassLoader classLoader = new URLClassLoader(urls);

Reflections reflections = new Reflections("com.autogenerate.data.common", classLoader);

Set<Class<? extends Object>> allClasses =
    reflections.getSubTypesOf(Object.class)
for(Class c:allClasses) {
  println "Class Name : " + c.getName()
}

}
}

task auto(type: AutogenTask)