jeudi 8 mars 2018

Field#getGenericType() throws java.lang.TypeNotPresentException

I have plugin I am writing where I need to know the type of the List<Person> where Person is an object defined within a dependency.

The Person class:

package com.dependency.models;    

public class Person {
    // Irrelevent
}

The use within the class I am performing the query on:

package com.project.wrappers;

import com.dependency.Person;    

@MyAnnotation
public class Wrapper {
     List<Person> people;
     // Other irrelevant stuff
}

The annotation:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
public @interface MyAnnotation {}

From within a Maven plugin use context I load all the Wrapper objects via annotation and then process. To do this I need to use a custom classloader via the Reflections library:

public static Set<Class<?>> findAnnotatedClasses(MavenProject mavenProject, Class<? extends Annotation> input) throws MojoExecutionException {
    List<String> classpathElements = null;

    try {
        classpathElements = mavenProject.getCompileClasspathElements();
        List<URL> projectClasspathList = new ArrayList<URL>();
        for (String element : classpathElements) {
            Application.getLogger().debug("Considering compile classpath element (via MavenProject): " + element);
            try {
                projectClasspathList.add(new File(element).toURI().toURL());
            } catch (MalformedURLException e) {
                throw new MojoExecutionException(element + " is an invalid classpath element", e);
            }
        }

        // Retain annotations
        JavassistAdapter javassistAdapter = new JavassistAdapter();
        javassistAdapter.includeInvisibleTag = false;

        URLClassLoader urlClassLoader = new URLClassLoader(projectClasspathList.toArray(new URL[]{}),
                Thread.currentThread().getContextClassLoader());

        Reflections reflections = new Reflections(
                new ConfigurationBuilder().setUrls(
                        ClasspathHelper.forClassLoader(urlClassLoader)
                ).addClassLoader(urlClassLoader).setScanners(new TypeAnnotationsScanner(), new TypeElementsScanner(),
                        new FieldAnnotationsScanner(), new TypeAnnotationsScanner(), new SubTypesScanner(false)
                ).setMetadataAdapter(javassistAdapter)
        );

        return findAnnotatedClasses(reflections, input);

    } catch (DependencyResolutionRequiredException e) {
        throw new MojoExecutionException("Dependency resolution failed", e);
    }
}

Everything up to here works great. But if I try the following, within my parser, it fails:

Field field = Wrapper.class.getDeclaredField("people");
ParameterizedType listType = (ParameterizedType) field.getGenericType();
Class<?> listTypeClass = (Class<?>) listType.getActualTypeArguments()[0];

The following exception is thrown:

Caused by: java.lang.TypeNotPresentException: Type com.dependency.Person not present
at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at sun.reflect.generics.repository.FieldRepository.getGenericType(FieldRepository.java:85)
at java.lang.reflect.Field.getGenericType(Field.java:247)

Which means that my classloading is working for everything prior to this last step. I think I need a way to override the classloader used within the sun.reflect library but it may not be possible. Any recommendations on a better approach?





Aucun commentaire:

Enregistrer un commentaire