David Balme David Balme - 23 days ago 6
Java Question

How to have a java annotation processor read and process annotations from projectA and generate java source files for projectB

I have created a java annotation that marks some of my 'service' classes as services and then written an annotation processor that generates corresponding servicerequest and servicehandler classes. This is for a GWT maven project where service requests are sent down to the server to a central dispatcher to be handle by the various services.

My maven project is composed of a parent project and 5 sub module projects.

The project structure is something like this:


  • MoodleMobile ( parent maven project )

    • gwtMoodleWS ( GWT layer )

    • moodleBuildSupport (contains annotation processor)

    • moodleGeneratedServiceHandlers (desired location of generated classes)

    • moodleWS ( server side layer )

    • moodleWSAPI ( location of code where annotation processor will run )




My question is this.

What is the prescribed way to have an annotation processor read and process annotations from one project (moodleWSAPI) and produce java source files in another project (moodleGeneratedServiceHandlers)?

I have seem many many examples of annotation processors produce java source files for the same project in which the annotation processor is running, but not for a different project.

I have successfully built and tested my annotation processor but I can't figure how to get it to produce the files outside of the project that the processor is running in. At the moment its producing the java source files within moodleWSAPI (this is wrong). I want it to process the source files in moodleWSAPI and then output the new generated source files in moodleGeneratedServiceHandlers.

Here is the code snippet that I have to create the actual java source file ..

public void createSourceFileFromTemplate(Template template, ClassSignature classSignature, VelocityContext context, Element element) {
PrintWriter pw = null;
try {
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(classSignature.getFullName(), classSignature.getElement());
OutputStream fileStream = sourceFile.openOutputStream();
pw = new PrintWriter(fileStream);
template.merge(context, pw);
} catch (Exception e) {
e.printStackTrace();
if (element!=null) {
processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(),element);
} else {
processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage());
}
} finally {
if (pw!=null) {
pw.close();
}
}
}


Please note: ClassSignature is just my custom class I use to capture class name of a class. I use Velocity as my template engine for producing the actual java source.

I don't see anything in the processingEnv.getFiler() spec that allows me to specify output outside of the project.

I could go the route of not using the processingEnv.getFiler().createSourceFile(..) and just build the java source files using simple java file IO. This would be trivially easy to do, but I am trying to color within the lines, so to speak, and accomplish my needs in the prescribed way. Any ideas ?

Answer

I don't think there is any special API for creating files for use in other projects. As far as the processor is concerned (or even Java in general) there are no such things as "projects" - only the input and output paths that have been provided to the compiler are known.

Using a Filer and StandardLocation helps with portability of the processor and avoids having to worry about the development environment's directory structure. If the processor will only be used in this single project, this kind of portability isn't really required and there should be no harm in using direct file io, because you know exactly how your modules are structured.

To make this a little more idiomatic you could pass the locations of the other project's source directories as compiler options to the annotation processor with the -Akey[=value] option. This would at least move any assumption about the other projects structures out of your code and shift the responsibility to maintain those paths to the build tool. Maven knows best how the projects are structure anyway, so I think this would be a good solution.