Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debug Java annotation processors using Intellij and Maven

I'm trying to learn how to make a custom annotation processor and I'm stuck with trying to debug it.

I have already managed to run the javac compiler in debug mode (with mvnDebug clean install) (with someone else's project with an annotation processor), connect to it with IntelliJ IDEA and have it stop at breakpoints in the annotation processor.


If we have something like this in some package in our project, being just like any other class (eg. no special configuration or anything):

public class MyProcessor extends AbstractProcessor {...}

Can we somehow hook it into the build process of maven as an annotation processor? So that it is compiled first, then the whole project is compiled with the annotation processor active.

Also, as far as I know, annotation processors require some kind of META INF file, which can be generated with something like google autoservices annotation processor.

So maybe a maven build process where we have autoservices run first, then the class extending the AbstractProcessor compiled as an annotation processor and finally have the whole project compile with our own annotation processor active (and with the javac compiler in debug mode).

like image 630
PaperTsar Avatar asked Jul 10 '15 16:07

PaperTsar


1 Answers

Here is the recipe.

Sidenote: I made it really detailed in some cases, skip the parts you already know how to do.

  1. First of all, download and install Maven, then download and install IntelliJ IDEA (referred to as IDEA from here on). (If you don't know how to use Windows CMD, here is a short tutorial for it, also: how to open the command prompt)

  2. Create a Maven project in IDEA without any Archetype. Then create some some package in src > main > java

  3. Create a Class which extends javax.annotation.processing.AbstractProcessor.

  4. Insert some minimal code, just to make it work. (Don't forget the Annotation at the top of the class declaration!)

    Assuming that the annotation full path is core.Factory, the code will look like

    @SupportedAnnotationTypes("core.Factory")
    public class MyProcessor extends AbstractProcessor {
    Messager messager;
    
        @Override
        public void init(ProcessingEnvironment env) {
            messager = env.getMessager();
            super.init(env);
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> annotations,       RoundEnvironment roundEnv) {
            for (TypeElement te : annotations)
                for (Element e : roundEnv.getElementsAnnotatedWith(te))
                    messager.printMessage(Diagnostic.Kind.NOTE, "Printing: " +   e.toString());
            return true;
        }
    
        @Override
        public SourceVersion getSupportedSourceVersion() {
            return SourceVersion.latestSupported();
        }
    }
    
  5. Create an annotation in the same package.

    public @interface Factory {
    
    }
    
  6. In the project there is probably a directory src > test > java, create there another package with the same name as the package you've created earlier. Then create a Class in it with a name ending with "Test" (for example: MyProcessorTest). Then annotate this class with the new annotation type you created earlier (@Factory).

    @Factory
    public class MyProcessorTest {
    
    }
    
  7. Now, for annotation processors to work, they have to have some file in META-INF. To achieve that, we'll use another annotation processor called autoservice. So in the pom.xml file insert it's dependency.

    <dependencies>
        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.0-rc2</version>
        </dependency>
    </dependencies>
    

    7.1 Side-note: For some reason, if I don't specify it explicitly, the Maven project uses Java 1.5. To force it to work with Java 1.8, insert this into the pom.xml file.

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
  8. Annotate our Processor Class with @AutoService(Processor.class).

  9. Now, we have to set up a remote debugger configuration in IDEA. To do that, go to Run > Edit Configurations, click on the green + button on the top left, select remote. Name it something like "mvnDebug", set the Host to localhost and the Port to 8000, press ok and it's good to go.

  10. Set a break point in the process method in our Processor.

  11. Open up the Windows command prompt, navigate to your projects directory, where the pom.xml resides. Then type in mvnDebug clean install.If everything has been set up right, it should say something like "Listening for transport dt_socket at address: 8000".

  12. Go back to IDEA and execute the mvnDebug configuration we've just made. If everything has been set up right, it should say something like "Connected to the target VM, address: 'localhost:8000', transport: 'socket'".

  13. Go back to the Command Prompt and if nothing is happening press some key to wake it up.

  14. If everything was set up right, IDEA will stop at the breakpoint, suspending javac's (the Java compiler) execution.


Additional tutorials on annotation processing

  • Annotation processing 101 - by Hannes Dorfmann.
like image 160
PaperTsar Avatar answered Oct 23 '22 02:10

PaperTsar