Webhooks4j
Small, simple and extendable Java library for messaging using webhooks and CDI events.
Status
Webhooks4j is currently under development.
Introduction
Webhooks4j is a simple Java library for implementing messaging using webhooks and event-sourcing, that does not need any infrastructure. It is meant to work for simple use cases where message brokers like Kafka are not needed. The library is based on the publish–subscribe pattern.
To subscribe to a topic, inject WebhookSubscriptions and call the subscribe method:
import com.github.jensborch.webhooks.Webhook;
import com.github.jensborch.webhooks.subscriber.WebhookSubscriptions;
@Inject
WebhookSubscriptions subscriptions;
Webhook webhook = new Webhook(new URI("http://publisher-host/context-root"), new URI("http://subscriber-host/context-root"), "my-topic");
subscriptions.subscribe(webhook.state(Webhook.State.SUBSCRIBE)); 
To publish events, inject a WebhookPublisher and call the publish method:
import com.github.jensborch.webhooks.WebhookEvent;
import com.github.jensborch.webhooks.publisher.WebhookPublisher;
@Inject
WebhookPublisher publisher;
Map<String, Object> eventData = new HashMap<>();
publisher.publish("my-topic", eventData)); 
To receive event use the CDI @Observes annotation:
import com.github.jensborch.webhooks.WebhookEvent;
import com.github.jensborch.webhooks.WebhookEventTopic;
public void observe(@Observes @WebhookEventTopic("my-topic") final WebhookEvent event) {
    //Process the event
} 
The library is build using CDI 1.2, JAX-RS 2.0, Jackson and SLF4J. Usage of SLF4J version 1.6.0 or higher is assumed.
CDI 1.2 is used to be compatible with as many application servers as possible. This imposes some constraints and the solution thus currently do not support asynchronous CDI events and generic event data.
Getting started
Added the following dependency for a subscriber:
<dependency>
    <groupId>com.github.jensborch.webhooks4j</groupId>
    <artifactId>webhooks4j-subscriber</artifactId>
    <version>0.6.5</version>
</dependency> 
and
<dependency>
    <groupId>com.github.jensborch.webhooks4j</groupId>
    <artifactId>webhooks4j-subscriber</artifactId>
    <version>0.6.5</version>
</dependency> 
for a publisher.
For MongoDB support add:
<dependency>
    <groupId>com.github.jensborch.webhooks4j</groupId>
    <artifactId>webhooks4j-mongodb-subscriber</artifactId>
    <version>0.6.5</version>
</dependency> 
and/or
<dependency>
    <groupId>com.github.jensborch.webhooks4j</groupId>
    <artifactId>webhooks4j-mongodb-publisher</artifactId>
    <version>0.6.5</version>
</dependency> 
If MongoDB is not use for persistence, it is necessary to implement WebhookEventStatusRepository and WebhookRepository repository interfaces.
The MongoDB dependency requires POJO support, see examples below.
CDI producers must be defined for:
- javax.ws.rs.client.Client
- com.github.jensborch.webhooks.WebhookTTLConfiguration (required for MongoDB, otherwise optional)
- com.github.jensborch.webhooks.subscriber.WebhookSyncConfiguration (required only by subscriber module)
- com.mongodb.client.MongoDatabase (for MongoDB support)
and the following REST exposure classes:
- com.github.jensborch.webhooks.subscriber.SubscriberEventExposure
- com.github.jensborch.webhooks.subscriber.SubscriberWebhooksExposure
- com.github.jensborch.webhooks.publisher.PublisherEventExposure
- com.github.jensborch.webhooks.publisher.PublisherWebhookExposure
should be registered in your JAX-RS application class, depending on how you do JAX-RS configuration.
Additionally it might be necessary to configure Jackson by implementing ContextResolver, as the Jdk8Module and JavaTimeModule are required.
An example application using Quarkus can be found in the Maven test module.
Examples
JAX-RS client producer:
@Dependent
public class ClientProducer {
    @Produces
    @Publisher
    public Client getPublisherClient() {
        return ClientBuilder.newClient();
    }
    @Produces
    @Subscriber
    public Client getSubscriberClient() {
        return ClientBuilder.newClient();
    }
} 
Mongo database producer:
@ApplicationScoped
public class WebhookMongoDBProducer {
    @Inject
    private MongoClient client;
    @Produces
    @Subscriber
    @Publisher
    public MongoDatabase mongoDatabase() {
         return client.getDatabase("MyDatabase");
    }
} 
Note, this requires an additional CDI producer for MongoClient, but it is possible to configure a MongoClient directly instead. If no codecs for ZonedDateTime and URI exists, register the following codec class:
- com.github.jensborch.webhooks.mongodb.URICodec
- com.github.jensborch.webhooks.mongodb.ZonedDateTimeCode
JAX-RS application class:
@ApplicationPath("/")
public class MyApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<>(Arrays.asList(
                MyExposure.class,
                SubscriberEventExposure.class,
                SubscriberWebhooksExposure.class,
                PublisherEventExposure.class,
                PublisherWebhookExposure.class,
        ));
        return classes;
    }
} 
Jackson ContextResolver class:
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
    @Override
    public ObjectMapper getContext(final Class<?> objectType) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new Jdk8Module());
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper;
    }
} 
Security
All endpoints are secured using JAX-RS roles. To access subscriber end-point, the subscriber role is needed. To access publisher endpoints the publisher role is need. The are some exceptions to this, as a publisher is allowed to POST callback events to a subscriber end-point. Refer to the Swagger documentation for the publisher and subscriber for details.
When creating the JAX-RS Client CDI producer, filters should be added to handle security correctly. A simple HTTP Basic access authentication filter can be found in the Maven test module.
Building
The Webhooks4j is build using Maven.
To build the application run the following command:
./mvnw package 
Install the application in your local maven repository (required for running locally)
./mvnw install 
Start the test application using:
./mvnw compile -pl test quarkus:dev 
Call endpoints using VSCode RestClient or similar. See webhooks4j.http
Run mutation tests:
./mvnw eu.stamp-project:pitmp-maven-plugin:run 
Release to Maven central:
./mvnw release:clean release:prepare -Prelease
./mvnw release:perform -Prelease 
 JarCasting
 JarCasting