Java EE Custom Annotation Locator

Wednesday Nov 13, 2019

Introduction

Thinking about plugins in a Java EE webapp, I wanted a way to have an interface that my code would call to notify listeners of events.

As a POC, I created an annotation and interface to implement this pattern.

Create Objects

Plugin interface

First, we create an interface for a simple Java EE plugin. This is a simple POJO with one event method:

public interface IAfterComplete {
  public void afterComplete(String someValue);
}

We will call the afterComplete() method when the action is complete. For simplicity it takes one String argument.

Annotation interface

Next, we create the Java annotation. This lets us determine which beans implement our interface at runtime:

Retention(RUNTIME)
@Target(TYPE)
@Qualifier
public @interface AfterComplete {
}

If we create the EJB we want to discover and annotate it with AfterComplete, we will see how easily we can locate it in the web application context.

Plugin & Annotation implmenetation

Now, we create the plugin implementation. The implmentation is a simple Stateles EJB with our custom annotation implementing our custom interface:

@AfterComplete
@Stateless
public class AfterCompletePrinter implements IAfterComplete {
  public void afterComplete(String someValue) {
    System.out.println("After complete printer: " + someValue);
  }

We declare it stateless so it will get created by our container and put into the applicaton’s Java EE context.

Webapp code to find & call annotated EJB’s:

Now, in our Java EE app, we can locate all beans with our @AfterComplete annotation and call the plugin’s afterComplete method:

Set<Bean<?>> beans = beanManager
    .getBeans(Object.class, new AnnotationLiteral<AfterComplete>() {
    });

for (Bean<?> bean : beans) {
  response.getWriter().print(bean.getBeanClass());
  IAfterComplete iac = (IAfterComplete) beanManager.getReference(bean, IAfterComplete.class, beanManager.createCreationalContext(bean));
  iac.afterComplete("After complete called on " + iac);
}

We can use this method in a Servlet, REST endpoint, or whatever else we want to create callbacks for different methods.

Example - github

There is an example Servlet and 2 beans that have a discover-able annotation at https://github.com/mikebski/ejb-custom-annotations that is a Maven project that can be used for a demonstration.

Check it out and run mvn package to generate a war file, then deploy it and go to:

http://localhost:8080/ejb-custom-annotations-1.0/

The output should be:

class net.mikeski.ejb_annotations.AfterCompletePrinter class net.mikeski.ejb_annotations.AfterCompleteErrPrinter

and your console should have 2 log messages like this:

18:45:51,937 INFO stdout After complete printer: After complete called on Proxy for view class: net.mikeski.ejb_annotations.IAfterComplete of EJB: AfterCompletePrinter

18:45:51,939 ERROR stderr After complete called on Proxy for view class: net.mikeski.ejb_annotations.IAfterComplete of EJB: AfterCompleteErrPrinter