Kendal API

From the very beginning Kendal was designed to be open for new extensions. Here we present a short guide how to make your own custom functionality.

1. Create project

First thing that has to be done is obviously project creation. Although Kendal works in any kind of Java project, and using any build system, for simplicity reasons we are going to create a Maven project. This project will contain handler implementation and annotation definition. It will be also responsible for handler registration to Kendal.

2. Add dependencies

After project is created we have to add dependencies to it. In this manual we assume that we work with projects managed by Maven. In such case, it is very easy to add required dependencies. Just add this snipped to your pom.xml file:


    <dependencies>
      <dependency>
        <groupId>com.github.projectkendal</groupId>
        <artifactId>processor</artifactId>
        <version>LATEST_KENDAL_VERSION</version>
      </dependency>
    </dependencies>
  

Remember to replace LATEST_KENDAL_VERSION with the latest Kendal version that is available.
You can check it for example here: https://mvnrepository.com/artifact/com.github.projectkendal/Kendal

3. Disable annotation processing

To make SPI mechanism work correctly we have to disable annotation processing in our handler's project. It is usually done by adding -proc:none parameter when executing the compiler. It Maven it can be achieved by adding the following code to the pom.xml file:


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.8</source>
                  <target>1.8</target>
                  <!-- Disable annotation processing for ourselves. -->
                    <compilerArgument>-proc:none</compilerArgument>
                </configuration>
            </plugin>
        </plugins>
    </build>
  

4. Create handler

Whatever logic you may want to implement, you must attach it to the Kendal processing process. First step to do it is to create a class implementing kendal.api.KendalHandler<T extends Annotation> interface. This interface defines one method: void handle(Collection<Node> handledNodes, AstHelper helper). This method is called by processor for each registered handler. First parameter collection of nodes representing annotations. In runtime, that collection will contain all occurrences of annotations (or inheriting annotations) of type with which handler is connected via type parameter <T extends Annotation> declaration.

Example

Let's assume we want to create a handler for our awesome feature. This feature will rely on annotation @Awesome, that will be allowed to be put on classes declarations. This way, programmer will claim that he/she wants this class to be automatically refactored to be awesome. First step to achieve this is to create annotation and its corresponding handler:


      @Target(ElementType.TYPE)
      public @interface Awesome { }

      public class AwesomeHandler implements KendalHandler<Awesome> {

          @Override
          public void handle(Collection<Node> handledNodes, AstHelper helper) {
              // some logic
          }
      }
  

5. Register handler

Next step is to let Kendal know about your handler. You can do it using SPI mechanism. That is quite simple. It is enough to create in your project's resources directory a file: META-INF/services/kendal.api.KendalHandler. This file should contain a list of handlers implemented in the project, each in the new line.

Example

In the case of our example project, this file's content will be very simple:


      kendal.apiExperiments.handler.AwesomeHandler
  

After this is done, our project is fully functional Kendal handler.

6. Utilizing AstHelper

Now we can start implementing our awesome feature. As you already learned, method handle is the core of a handler. We know that first parameter is a collection of annotation nodes. Now it is time to learn about the second parameter - AstHelper helper. This is a very useful class, it aims at facilitating whatever you may want to do to AST tree. It delivers methods that allow AST nodes modification. It also shares some more specific helpers: AstNodeBuilder, AstValidator and AstUtils. They contain methods which are often needed in projects of this kind. Using them saves your time and lines of code.

7. Using own handler

After we finish our handler, we can start using it in other projects. In order to do this, we have to add some dependencies to the target project. These are com.github.projectkendal.processor and your own project.

Example

If we want to add our custom handler to some other application we have to add the following code to the pom.xml file of that application:


    <dependencies>
      <dependency>
        <groupId>com.github.projectkendal</groupId>
        <artifactId>processor</artifactId>
        <version>LATEST_KENDAL_VERSION</version>
      </dependency>
      <dependency>
        <groupId>your.package</groupId>
        <artifactId>awesome</artifactId>
        <version>AWESOME_HANDLER_VERSION</version>
      </dependency>
    </dependencies>
  

Then we can start using our annotation:


      @Awesome
      public class SomeClass {
          // logic
      }
  

Our AwesomeHandler will be run whenever there is at least one usage of @Awesome annotation in the target project. Each such usage will be included in handledNodes collection that is passed to handle method of our handler.