19

I have an annotation processor which shall generate a enumeration with the keys defined by getter methods of an interface.

The interface resides in

MyProject/src/main/java/my.package.MyInterfaces.java.

I want to validate the properties files which reside in MyProject/src/main/resources/META-INF/resource-bundle/ if they do contain the keys defined in the generated enum.

My problem is that the properties files are not available via the classloader of the processor or via Filer.getResource(...).

  • How can I make the source or classpath of this project available to the processor, so that I can load the properties files ?

  • Now I have only the resources within the jar where the processor resides available. I did try to define -classpath and/or -sourcepath via eclipse Project/Properties/Java compiler/Annotation processing/Processor options but it did not work out.

Has anyone faced an issue like this, and can anyone give me a clue as to how I can make the resources available for the processor?

I do have the maven structure but do not use maven, because of old dependencies within the application. So maven is now not an option for me.

Eclipse 3.6 Helios Seems that StandardLocation.SOURCE_PATH and StandardLocation.CLASS_PATH are not implemented in Filer#getResource(), so writing generated source or class files to SOURCE_PATH or CLASS_PATH seems not be possible, also accessing any files on SOURCE_PATH and CLASS_PATH*

Thanks.

Rockstar
  • 2,228
  • 3
  • 20
  • 39
CCHET
  • 199
  • 1
  • 4
  • Yes, it seems it is not implemented. Anyone a workaround? – Arne Deutsch Nov 16 '13 at 14:18
  • 1
    Did you every solve this one? I've a similar problem: http://stackoverflow.com/questions/29658252/how-to-obtain-the-right-javafilemanager-in-a-java-annotation-processor – Jurgen Vinju Apr 17 '15 at 11:35
  • More important than the source location is the location in your output folder. Did you check that the file is also in output/META-INF/resource-bundle? How do you build? What is your output folder and which path do you specify when loading the bundle? – Jürgen Sep 01 '16 at 22:35
  • Why not `MyProject/src/main/java/my/package/MyInterfaces.java.`? – Thorbjørn Ravn Andersen Jul 23 '17 at 06:24

3 Answers3

2

The issue is the ClassLoader that is bound to the current thread (Thread.currentThread().getContextClassLoader()) is not a URLClassLoader when Processor#process is called. It appears to be a restricted class loader that does not allowing loading resources. This happens with javac, eclipse compiler, maven compiler, etc.

Luckily your processor class will have an appropriate ClassLoader bound to it (ie getClass().getClassLoader()).

The problem is most utilities expect the correct ClassLoader to be bound to the thread (most notably the ServiceLoader and ResourceBundles).

Thus there is a work around. When your Processor executes you can rebind the ClassLoader to the current thread:

@Override
public boolean process(
        Set<? extends TypeElement> annotations,
        RoundEnvironment roundEnv) {

    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    //...
}

EDIT:

I have feeling a large amount of people are having the problem of resources that are in src/main/resources with Eclipse not loading them using the annotation processor Filer (throws an exception).

And indeed it is a well known issue that Eclipse will not load target/classes resources. The classpath hack I showed above may or may not work and was more for other libraries being used by the processor (as in they are already compiled with the resources).

The solution (if it could be called that) is to load the files like they are normal files. This requires guessing the project structure with some hacks.

OR

You can tell Eclipse users just to put the resources in src/main/java.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • I'm facing this issue right now with a processor just inside Eclipse. I tried your workaround but it still can't find the resource. Do you have any alternative? – Cristiano Apr 19 '20 at 23:11
  • @Cristiano Let me guess. You are trying to access resources that are usually in `src/main/resources`? The Eclipse annotation Filer is broken for that see this https://stackoverflow.com/questions/49631221/how-to-get-resouce-in-eclipse-jdt-apt-environment. – Adam Gent Mar 23 '22 at 16:39
0

As a workaround you could try to add the classpath you need to use via a command line argument -Xboothclasspath/a:path, the /a will append the value in path to the boot class path. You would need to add this as a command line option to the actual annotation processing run so in Eclipse that would be: Right click project, select properties, Java Compiler, Annotation Processing, click New in the table and add key -Xbootclasspath/a and the path to add as value. I'm afraid I haven't tried this for annotation processing but it's worth a try!

-1

My problem is that the properties files are not available via the classloader of the processor or via Filer.getResource(...).

I'm not sure I'm understanding your problem. But maybe something here will help.

How can I make the source or classpath of this project available to the processor, so that I can load the properties files ?

You need to add the src/main/resources as a "source folder" in Eclipse. First select Configure Build Path in Eclipse on your Java project. Then choose the Source tab and click on Add Folder. You should be able to select the src/main/resources folder and click Ok. You should now see the src/main/resources in the Source Folders list.

If you look into your target/classes directory, you should see all of the files from the resources directory in there which lets you know that they were copied into the classpath correctly.

# files in the src main resources
> ls src/main/resources/x/y/z
jgroups_udp.xml
# should compile into target/classes
> ls target/classes/x/y/z
jgroups_udp.xml org
# and should show up in the jar
> -tvf target/project.jar 
   0 Thu Nov 03 18:50:00 EDT 2016 META-INF/
 135 Thu Nov 03 18:49:58 EDT 2016 META-INF/MANIFEST.MF
 ...
3036 Thu Nov 03 18:49:36 EDT 2016 x/y/z/jgroups_udp.xml

Then in your code you can reference the file by doing the following. This will load the file from the top of the classpath. If it is in a subdir then you lead with:

InputStream stream =
    getClass().getClassLoader().getResourceAsStream("x/y/z/jgroups_udp.xml");

Btw, if you were using maven, you would add something like the following to you pom.xml:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
</build>
Gray
  • 115,027
  • 24
  • 293
  • 354